为什么sql server需要先把count(*)结果转换成int再与int变量比较?

sou*_*ser 12 sql-server-2008 sql-server

我的应用程序中有很多查询,其中在 have 子句中,我将 count 聚合函数与 int 变量进行了比较。在查询计划中,我可以在比较之前看到一个implicit_convert。我想知道为什么会发生这种情况,因为根据 sql server 文档,count 函数的返回类型是 int。那么为什么要进行隐式转换来比较两个 int 值呢?

以下是一个这样的查询计划的一部分,其中 @IdCount 被定义为一个 int 变量。

|--过滤器(WHERE:([Expr1022]=[@IdCount]))    
 |--计算标量(DEFINE:([Expr1022]=CONVERT_IMPLICIT(int,[Expr1028],0))) 
  |--Stream Aggregate(GROUP BY:([MOCK_DB].[dbo].[Scope].[ScopeID]) DEFINE:([Expr1028]=Count(*)))

Mar*_*ith 17

您将其与integer变量进行比较的事实无关紧要。

的计划COUNT总是有一个CONVERT_IMPLICIT(int,[ExprNNNN],0))whereExprNNNN是表示 的结果的表达式的标签COUNT

我的假设一直是该代码COUNT只是最终调用相同的代码COUNT_BIG和演员是必要的转换bigint是回落到的结果int

事实上COUNT_BIG(*),在查询计划中甚至没有从COUNT(*). 两者都显示为Scalar Operator(Count(*))

COUNT_BIG(nullable_column)在执行计划中确实得到了区分,COUNT(nullable_column) 但后者仍然隐式转换回int.

下面是一些证明这种情况的证据。

WITH 
E1(N) AS 
(
    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
)                                       -- 1*10^1 or 10 rows
, E2(N) AS (SELECT 1 FROM E1 a, E1 b)   -- 1*10^2 or 100 rows
, E4(N) AS (SELECT 1 FROM E2 a, E2 b)   -- 1*10^4 or 10,000 rows
, E8(N) AS (SELECT 1 FROM E4 a, E4 b)   -- 1*10^8 or 100,000,000 rows
, E16(N) AS (SELECT 1 FROM E8 a, E8 b)  -- 1*10^16 or 10,000,000,000,000,000 rows
, T(N) AS (SELECT TOP (2150000000) 
                  ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS N FROM E16)
SELECT COUNT(CASE WHEN N < 2150000000 THEN 1 END)
FROM T 
OPTION (MAXDOP 1)
Run Code Online (Sandbox Code Playgroud)

这需要大约 7 分钟才能在我的桌面上运行并返回以下内容

Msg 8115, Level 16, State 2, Line 1
将表达式转换为数据类型 int 时出现算术溢出错误。
警告:空值被聚合或其他 SET 操作消除。

这表明COUNT在 aint溢出(在 2147483647 处)并且最后一行 (2150000000) 被COUNT操作员处理后,必须继续进行,从而导致有关NULL返回的消息。

通过比较的COUNT方式用SUM(CASE WHEN N < 2150000000 THEN 1 END)返回替换表达式

Msg 8115, Level 16, State 2, Line 1
将表达式转换为数据类型 int 时出现算术溢出错误。

没有ANSI警告NULL。从中我得出结论,在达到第 2,150,000,000 行之前,在聚合过程中发生了溢出。