Neden sql sunucusu int değişkeni ile karşılaştırılmadan önce count (*) sonucunu int'e dönüştürmek gerekir?


11

Benim uygulama birçok sahip var nerede sahip yan tümcesinde, sayım toplama işlevi int değişken ile karşılaştırma var. Sorgu planlarında, karşılaştırmadan önce bir implicit_convert görebilirsiniz. Neden böyle olduğunu bilmek istiyorum çünkü sql sunucu belgelerine göre sayım işlevinin dönüş türü int. Öyleyse neden iki int değerinin karşılaştırılması için örtük bir dönüşüm olmalı?

Aşağıda @IdCount bir int değişkeni olarak tanımlanmış böyle bir sorgu planının bir parçasıdır.

| --Filter (WHERE ([Expr1022] = [@ IDCount]))    
 | --Bilgisayar Skaler (DEFINE: ([Expr1022] = CONVERT_IMPLICIT (int, [Expr1028], 0))) 
  | --Stream Aggregate (GROUP BY: ([MOCK_DB]. [Dbo]. [Kapsam]. [KapsamKimliği]) DEFINE: ([Expr1028] = Sayı (*)))

Yanıtlar:


17

Bir integerdeğişkeni karşılaştırdığınız gerçeği önemsizdir.

Planı COUNTher zaman vardır CONVERT_IMPLICIT(int,[ExprNNNN],0))nereye ExprNNNNsonucunu temsil eden ifadesi için etikettir COUNT.

Benim varsayım her zaman için kod COUNTsadece aynı kodu çağırarak sona erdi COUNT_BIGve alçı bigintsonucu geri dönüştürmek için gerekli int.

Aslında COUNT_BIG(*)sorgu planından bile ayırt edilemez COUNT(*). Her ikisi de olarak görünür Scalar Operator(Count(*)).

COUNT_BIG(nullable_column)yürütme planından ayırt edilir COUNT(nullable_column) ancak ikincisi hala örtük bir rol alır int.

Durumun bu olduğuna dair bazı kanıtlar aşağıdadır.

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)

Bu, masaüstümde yaklaşık 7 dakika sürer ve aşağıdakileri döndürür

Msg 8115, Seviye 16, Durum 2, Satır 1
İnt ifadesini veri türüne dönüştüren aritmetik taşma hatası.
Uyarı: Boş değer, bir küme veya başka bir SET işlemi ile ortadan kaldırılır.

Bu , taşmanın (2147483647'de) ve son satırın (2150000000), döndürülmesiyle ilgili iletiye yönlendiren operatör tarafından işlendikten COUNTsonra devam etmesi gerektiğini gösterir .intCOUNTNULL

COUNTİfadeyi SUM(CASE WHEN N < 2150000000 THEN 1 END)döndürmeyle değiştirerek karşılaştırma yoluyla

Msg 8115, Seviye 16, Durum 2, Satır 1
İnt ifadesini veri türüne dönüştüren aritmetik taşma hatası.

ANSIhakkında hiçbir uyarı ile NULL. Buradan, 2.150.000.000. Sıraya ulaşılmadan önce toplama sırasında bu taşma gerçekleştiği sonucuna vardım.


@PaulWhite - Teşekkürler. XML'ye bakmalıydım. ScalarOperatorSSMS özellikleri penceresinde gösterilen değere bakıyordum .
Martin Smith
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.