Perché il server sql deve convertire il risultato count (*) in int prima di confrontarlo con una variabile int?


11

Ho molte domande nella mia applicazione in cui nella clausola having, ho un confronto della funzione di aggregazione conteggio con la variabile int. Nei piani di query, posso vedere un implicit_convert prima del confronto. Voglio sapere perché questo accade perché secondo la documentazione del server sql, il tipo restituito della funzione count è int. Quindi perché dovrebbe esserci una conversione implicita per il confronto di due valori int?

Di seguito è parte di uno di questi piani di query in cui @IdCount è definito come una variabile int.

| --Filter (DOVE: ([Expr1022] = [@ IdCount]))    
 | --Compare Scalar (DEFINE: ([Expr1022] = CONVERT_IMPLICIT (int, [Expr1028], 0))) 
  | --Stream Aggregate (GROUP BY: ([MOCK_DB]. [Dbo]. [Scope]. [ScopeID]) DEFINE: ([Expr1028] = Count (*)))

Risposte:


17

Il fatto che tu lo stia confrontando con una integervariabile è irrilevante.

Il piano per COUNTha sempre un CONVERT_IMPLICIT(int,[ExprNNNN],0))dove ExprNNNNè l'etichetta per l'espressione che rappresenta il risultato di COUNT.

La mia ipotesi è sempre stata che il codice per COUNTfinire semplicemente chiamando lo stesso codice COUNT_BIGe il cast è necessario per convertire il bigintrisultato di quel back down in int.

In realtà COUNT_BIG(*)non è nemmeno distinto nel piano di query da COUNT(*). Entrambi si presentano come Scalar Operator(Count(*)).

COUNT_BIG(nullable_column)si distingue nel piano di esecuzione, COUNT(nullable_column) ma quest'ultimo ottiene ancora un cast implicito int.

Alcune prove del fatto che questo è il caso sono di seguito.

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)

Questo richiede circa 7 minuti per essere eseguito sul mio desktop e restituisce quanto segue

Messaggio 8115, livello 16, stato 2, riga 1
Errore di overflow aritmetico durante la conversione dell'espressione nel tipo di dati int.
Avvertenza: il valore null viene eliminato da un'operazione aggregata o altra operazione SET.

Il che indica che il COUNTmust deve essere continuato dopo che intsarebbe stato traboccato (a 2147483647) e l'ultima riga (2150000000) è stata elaborata COUNTdall'operatore che ha portato al messaggio di NULLrestituzione.

A titolo di confronto, sostituire l' COUNTespressione con i SUM(CASE WHEN N < 2150000000 THEN 1 END)ritorni

Messaggio 8115, livello 16, stato 2, riga 1
Errore di overflow aritmetico durante la conversione dell'espressione nel tipo di dati int.

senza alcun ANSIpreavviso NULL. Da cui concludo l'overflow si è verificato in questo caso durante l'aggregazione stessa prima che fosse raggiunta la riga 2.150.000.000.


@PaulWhite - Grazie. Avrei dovuto guardare l'XML. Stavo guardando il ScalarOperatorvalore mostrato nella finestra delle proprietà SSMS.
Martin Smith,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.