算术溢出错误在where子句中使用int变量转换十进制(bug?)

时间:2015-03-25 17:02:44

标签: sql-server-2012

我认为这是SQL Server(2012)中的一个奇怪的错误,但请帮助我进行健全性检查。这是我们的一个存储过程中的丑陋查询的过度简化版本(真正的查询有13个表)。 Table3.r是十进制(12,2)。

DECLARE @elid int, @bpid int, @tid int, @r decimal(14, 10);
SELECT @elid = 123, @bpid = 456, @tid = -1;
SELECT TOP 1 @r = t3.r -- implicitly converts dec 12,2 to dec 14,10
FROM Table1 t1
    INNER JOIN Table2 t2 ON t1.blah = t2.blah and t2.elid = @elid
    INNER JOIN Table3 t3 ON t2.wth = t3.wth and t2.tid = t3.tid
WHERE t1.bpid = @bpid
    AND t3.tid > @tid
ORDER BY t3.tid;

样本表定义:

CREATE TABLE Table1 (bpid int IDENTITY(1, 1), blah int, ...)
CREATE TABLE Table2 (elid int IDENTITY(1, 1), blah int, wth int, tid int...)
CREATE TABLE Table3 (..., wth int, tid int, r decimal(12, 2))

表3有大约80k的记录。 99.999%的查询时间。但至少在一个奇怪的情况下,t3.r = 24.79(尽管我不相信值24.79本身是有问题的),它会导致这个错误:

  

将数字转换为数据类型数字

的算术溢出错误

然后我发现如果你只是在WHERE子句中硬编码-1就可以正常工作:

....
    AND t3.tid > -1;

或者,如果您只选择t3.r(而不是尝试转换为14,10),它也可以正常工作:

SELECT TOP 1 t3.r
...
    AND t3.tid > @tid; -- var is still in the WHERE clause

或者,如果将@tid设置为仅生成单个记录的值(通常返回3或4条记录,然后抓取TOP 1),它也会起作用:

SELECT @elid = 123, @bpid = 456, @tid = 9;
SELECT TOP 1 ...

我知道你在想“只是制作@r 12,2”而且是的,但是我会为你省去为什么它不那么简单的细节。然而,制作@r 20,10是一个可行的选择并确实解决了这个问题。

我发誓我并不疯狂,我希望我能发布视频来证明这种情况正在发生。我以前看过变量的奇怪行为,但没有这样的。有任何想法吗?这听起来像是一个不起眼的错误吗?

这是真正的查询(被破坏的表格名称,告诉你它很难看):

SELECT TOP 1 @r = pc.pc_cost
--SELECT TOP 1 pc.pc_cost, CONVERT(decimal(14, 10), pc.pc_cost)
from BP
join EL on bp_id=el_bpid and el_id = 65232907
join PL on bp_plid=pl_id and pl_type=1 and pl_exchange=1
join BU on bu_id=bp_buid
join SL ON sl_id=bu_slid and sl_subtype=pl_subtype and sl_plantype=1
join ER on er_eeid=el_eeid and er_cgeid>1 and er_cgeid>1
join XR on er_id=xr_erid
join EG on sl_egid=eg_id
join SM on sm_eid=eg_eid and sm_egid=eg_id and sm_subtype= er_subtype
join XS on xs_id=sm_xsid and xs_phase>1
join XD on xd_xsid=xs_id
join CN on cn_id=bp_cnid
join PC on pc_cnid=cn_id and pc_tierid=xd_tierid and er_id = pc_erid
where bp_id= 314853
and pc_tierid > @tid
order by pc_tierid ASC;

1 个答案:

答案 0 :(得分:0)

这些问题一直让我感兴趣所以我试图重现你所看到的问题。到目前为止,下面的代码是我获得此错误的唯一成功结果:

use demo 
go
if object_id('dbo.numbers', 'U') is not null
    drop table dbo.numbers
create table numbers (
    nid int primary key
   ,d12_2 decimal(12, 2))
insert dbo.numbers
select * from (values 
(1, 1234567890.12),
(2, 12345)) i(n,d)
go
declare @d14_10 decimal(14, 10)
select @d14_10 = (select top 1 d12_2
                    from dbo.numbers
                    where nid = 1)
go
declare @d14_10 decimal(14, 10)
select @d14_10 = (select top 1 d12_2
                    from dbo.numbers
                    where nid = 2)

使用以下输出:

Msg 8115, Level 16, State 8, Line 15
Arithmetic overflow error converting numeric to data type numeric.
Msg 8115, Level 16, State 8, Line 20
Arithmetic overflow error converting numeric to data type numeric.

我能够获得此错误的唯一方法是尝试转换不适合新类型的东西(duh ...)因为精度在增加(点后面的空格),所以无法获得陷入困境。这让我得出结论,您的数据中必须有一条记录,其中包含t3.r => 10000

这一开始看起来很奇怪,因为你有一个已知的t3.r值,它更小。但是......我想知道你是如何确定这个价值的?是否有可能24.79是最后一次成功运行的值?

唯一的另一个选择是问题是在您的逐渐展示的示例范围之外引起的。