我们有如下查询
SELECT p.propertyNumber as propertyNumber,
( SELECT COUNT(*)
FROM #TEM
WHERE pNumber = p.propertyNumber
AND dsenderType = 0
AND dType = 1
AND reTime >= '2017-03-01 00:00'
AND reTime <= '2017-04-01 00:00' ) AS temailCount,
( SELECT COUNT(*)
FROM #TEM
WHERE pNumber = p.propertyNumber
AND dsenderType = 1
AND dType = 1
AND dSendStatus = 1
AND sentTime >= '2017-03-01 00:00'
AND sentTime <= '2017-04-01 00:00' ) AS temailJobCount,
( SELECT SUM( DATEDIFF( second, reTime, jEnddate ) )
FROM #TEM
WHERE pNumber = p.propertyNumber
AND dsenderType = 0
AND dType = 1
AND ( jStatus = 2 OR jStatus = 4 )
AND jEnddate >= '2017-03-01 00:00'
AND jEnddate <= '2017-04-01 00:00' ) AS temailturnAroundTime
FROM property p
WHERE p.locationId = 6
GROUP BY propertyNumber
抛出“将表达式转换为数据类型int的算术溢出错误”错误。但是如果我们将SUM( DATEDIFF( second, reTime, jEnddate ) )
更改为SUM( DATEDIFF( minute, reTime, jEnddate ) )
并且最大数字是1153447分钟(不超过70000000秒),它就会有效。
最奇怪的是,如果我们通过将SUM
函数作为第二个字段来更改查询顺序,那么它是有效的(如果SUM
是唯一的字段,也可以工作)
SELECT p.propertyNumber AS propertyNumber,
( SELECT SUM( DATEDIFF( second, reTime, jEnddate ) )
FROM #TEM
WHERE pNumber = p.propertyNumber
AND dsenderType = 0
AND dType = 1
AND ( jStatus = 2 OR jStatus = 4 )
AND jEnddate >= '2017-03-01 00:00'
AND jEnddate <= '2017-04-01 00:00' ) AS temailturnAroundTime,
( SELECT ......) AS temailCount,
( SELECT ......) AS temailJobCount
FROM property p
WHERE p.locationId = 6
GROUP BY propertyNumber
我们可以通过将DATEDIFF
结果转换为bigint
来解决问题,但我仍然无法弄清楚错误发生的原因。任何人都可以给我一个线索吗?
非常感谢!
答案 0 :(得分:1)
当服务器逐行读取以计算SUM
时,它会在内部int
变量中将int
值相加。如果在求和期间中间结果超过int
容量(2,147,483,647),服务器将停止计算并抛出您看到的错误。
即使后续值为负数且总和将小于2,147,483,647,服务器也将停止查询执行。
显然,在查询的第一个版本中,服务器按某种顺序扫描行,这导致累积的中间结果超过2,147,483,647。其他版本的查询可能使用不同的索引来扫描行,因此,值的总和不同,从不超过2,147,483,647。
很容易重现。
我将创建一个包含聚簇索引的表,以不同的顺序添加几行并计算SUM
。
在这个简单的查询中,服务器将使用聚簇索引按照此索引的顺序扫描表。
CREATE TABLE [dbo].[T](
[ID] [int] NOT NULL,
[Value] [int] NOT NULL,
CONSTRAINT [PK_T] PRIMARY KEY CLUSTERED
(
[ID] ASC
))
TRUNCATE TABLE [dbo].[T];
INSERT [dbo].[T] ([ID], [Value]) VALUES (1, 2147483647);
INSERT [dbo].[T] ([ID], [Value]) VALUES (2, -2147483647);
INSERT [dbo].[T] ([ID], [Value]) VALUES (3, 2147483647);
INSERT [dbo].[T] ([ID], [Value]) VALUES (4, -2147483647);
SELECT SUM([Value]) AS s
FROM [dbo].[T];
<强>结果强>
(1 row(s) affected)
(1 row(s) affected)
(1 row(s) affected)
(1 row(s) affected)
(1 row(s) affected)
+---+
| s |
+---+
| 0 |
+---+
TRUNCATE TABLE [dbo].[T];
INSERT [dbo].[T] ([ID], [Value]) VALUES (1, 2147483647);
INSERT [dbo].[T] ([ID], [Value]) VALUES (2, 2147483647);
INSERT [dbo].[T] ([ID], [Value]) VALUES (3, -2147483647);
INSERT [dbo].[T] ([ID], [Value]) VALUES (4, -2147483647);
SELECT SUM([Value]) AS s
FROM [dbo].[T];
<强>结果强>
(1 row(s) affected)
(1 row(s) affected)
(1 row(s) affected)
(1 row(s) affected)
Msg 8115, Level 16, State 2, Line 8
Arithmetic overflow error converting expression to data type int.
避免此问题的最简单方法是将值转换为bigint
,如您自己发现的那样:
TRUNCATE TABLE [dbo].[T];
INSERT [dbo].[T] ([ID], [Value]) VALUES (1, 2147483647);
INSERT [dbo].[T] ([ID], [Value]) VALUES (2, 2147483647);
INSERT [dbo].[T] ([ID], [Value]) VALUES (3, -2147483647);
INSERT [dbo].[T] ([ID], [Value]) VALUES (4, -2147483647);
SELECT SUM(CAST([Value] AS bigint)) AS s
FROM [dbo].[T];
此查询返回0而没有算术溢出错误。
发生此错误时可能会出现其他情况。
服务器可以先计算DATEDIFF(second,reTime,jEnddate)
表达式的值,然后根据WHERE
子句筛选出该行。
因此,如果表中有一行存在较大差异,即使稍后将其过滤掉,也可能会影响计算。
引擎不必先过滤掉行,然后再计算表达式。 您可以通过检查实际执行计划来了解正在发生的事情。
最有可能的是,当您更改查询时,计划会以这样的方式更改,即在计算表达式之前过滤掉具有较大差异的行。
这样做很容易检查:
select
MAX(DATEDIFF(minute,reTime,jEnddate)) AS MaxDiff,
MIN(DATEDIFF(minute,reTime,jEnddate)) AS MinDiff
from #TEM
请注意,此查询中没有WHERE
。您希望看到全局MAX
和MIN
差异。