Mengapa sql server perlu mengkonversi hasil count (*) menjadi int sebelum membandingkannya dengan variabel int?

11

Saya memiliki banyak pertanyaan dalam aplikasi saya di mana dalam klausa yang memiliki, saya memiliki perbandingan fungsi agregat hitung dengan variabel int. Dalam paket kueri, saya bisa melihat implisit_convert sebelum perbandingan. Saya ingin tahu mengapa ini terjadi karena sesuai dengan dokumentasi sql server, tipe kembalinya fungsi hitungan adalah int. Jadi mengapa harus ada konversi implisit untuk perbandingan dua nilai int?

Berikut ini adalah bagian dari satu rencana kueri di mana @IdCount didefinisikan sebagai variabel int.

| --Filter (DIMANA: ([Expr1022] = [@ IdCount]))    
 | --Komputasi Skalar (DEFINE: ([Expr1022] = CONVERT_IMPLICIT (int, [Expr1028], 0)))) 
  | --Stream Aggregate (GROUP BY: ([MOCK_DB]. [Dbo]. [Scope]. [ScopeID]) DEFINE: ([Expr1028] = Count (*)))
souser
sumber

Jawaban:

17

Fakta bahwa Anda membandingkannya dengan suatu integervariabel tidak relevan.

Rencana untuk COUNTselalu memiliki di CONVERT_IMPLICIT(int,[ExprNNNN],0))mana ExprNNNNadalah label untuk ekspresi yang mewakili hasil dari COUNT.

Asumsi saya selalu bahwa kode untuk COUNTakhirnya hanya memanggil kode yang sama COUNT_BIGdan para pemain diperlukan untuk mengkonversi biginthasil itu kembali ke int.

Bahkan COUNT_BIG(*)tidak dibedakan dalam rencana kueri dari COUNT(*). Keduanya muncul sebagai Scalar Operator(Count(*)).

COUNT_BIG(nullable_column)tidak dibedakan dalam rencana eksekusi dari COUNT(nullable_column) tetapi yang terakhir masih mendapat pemeran implisit kembali ke int.

Beberapa bukti bahwa ini adalah kasus di bawah ini.

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)

Ini membutuhkan waktu sekitar 7 menit untuk berjalan di desktop saya dan mengembalikan yang berikut

Msg 8115, Level 16, Negara 2, Baris 1
Kesalahan aritmatika overflow mengubah ekspresi ke int tipe data.
Peringatan: Nilai kosong dihilangkan dengan agregat atau operasi SET lainnya.

Yang menunjukkan bahwa COUNTharus dilanjutkan setelah intakan meluap (pada 2147483647) dan baris terakhir (2150000000) diproses oleh COUNToperator yang mengarah ke pesan tentang NULLdikembalikan.

Dengan cara perbandingan mengganti COUNTekspresi dengan SUM(CASE WHEN N < 2150000000 THEN 1 END)pengembalian

Msg 8115, Level 16, Negara 2, Baris 1
Kesalahan aritmatika overflow mengubah ekspresi ke int tipe data.

tanpa ANSIperingatan NULL. Dari yang saya simpulkan, limpahan yang terjadi dalam kasus ini selama agregasi sendiri sebelum baris 2.150.000.000 tercapai.

Martin Smith
sumber
@ PaulWhite - Terima kasih. Saya seharusnya melihat XML. Saya melihat ScalarOperatornilai yang ditampilkan di jendela properti SSMS.
Martin Smith