SQLサーバーは、count(*)の結果をint変数と比較する前にintに変換する必要があるのはなぜですか?


11

私のアプリケーションには多くのクエリがあり、having句でcount集計関数とint変数を比較しています。クエリプランでは、比較の前にimplicit_convertを確認できます。SQLサーバーのドキュメントに従って、カウント関数の戻り値の型がintであるため、これが発生する理由を知りたいです。では、なぜ2つのint値を比較するための暗黙の変換が必要なのでしょうか。

以下は、@ IdCountがint変数として定義されているそのようなクエリプランの一部です。

| --Filter(WHERE:([Expr1022] = [@ IdCount]))    
 |-スカラー計算(DEFINE:([Expr1022] = CONVERT_IMPLICIT(int、[Expr1028]、0))) 
  | --Stream Aggregate(GROUP BY:([MOCK_DB]。[dbo]。[Scope]。[ScopeID])DEFINE:([Expr1028] = Count(*)))

回答:


17

integer変数と比較しているという事実は関係ありません。

のプランにはCOUNT常にCONVERT_IMPLICIT(int,[ExprNNNN],0))where ExprNNNNがあり、はの結果を表す式のラベルです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)

これは私のデスクトップで実行するのに約7分かかり、次を返します

メッセージ8115、レベル16、状態2、行1
式をデータ型intに変換する算術オーバーフローエラー。
警告:Null値は、集計またはその他のSET操作によって削除されます。

これは、がオーバーフローCOUNTした後int(2147483647)、最後の行(2150000000)がCOUNTオペレーターによって処理され、NULL返されたというメッセージが表示された後も、が継続している必要があることを示しています。

比較のために置き換えCOUNTて表現をSUM(CASE WHEN N < 2150000000 THEN 1 END)返します

メッセージ8115、レベル16、状態2、行1
式をデータ型intに変換する算術オーバーフローエラー。

ANSIに関する警告なしNULL。この場合、行自体が行2,150,000,000に達する前の集計中にオーバーフローが発生したと結論付けます。


@PaulWhite-ありがとう。私はXMLを見るべきだった。ScalarOperatorSSMSのプロパティウィンドウに表示される値を見ていました。
マーティンスミス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.