Чому серверу sql необхідно перетворити результат count (*) в int, перш ніж порівнювати його зі змінною int?


11

У моїй програмі є багато запитів, де у клавіші, що має, я порівнюю функцію підрахунку сукупності з змінною int. У планах запитів я бачу implicit_convert перед порівнянням. Я хочу знати, чому це відбувається, оскільки відповідно до документації на сервер sql функція повернення типу count повертається до int. То чому б мала відбуватися неявна конверсія для порівняння двох значень int?

Далі йде частина одного такого плану запитів, де @IdCount визначається як змінна int.

| --Filter (ДЕ: ([Expr1022] = [@ IdCount]))    
 | --Campute Scalar (DEFINE: ([Expr1022] = CONVERT_IMPLICIT (int, [Expr1028], 0))) 
  | - Агрегат трансляції (GROUP BY: ([MOCK_DB]. [Dbo]. [Scope]. [ScopeID]) DEFINE: ([Expr1028] = Count (*)))

Відповіді:


17

Те, що ви порівнюєте його зі integerзмінною, не має значення.

План COUNTзавжди має те, CONVERT_IMPLICIT(int,[ExprNNNN],0))де 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 хвилин і повертає наступне

Msg 8115, рівень 16, стан 2, рядок 1
Арифметична помилка переповнення перетворення виразу в тип даних int.
Попередження: Нульове значення усувається за допомогою сукупної або іншої операції SET.

Що вказує на те, що COUNTнеобхідно продовжити після того, як intпереповнюється (на 2147483647), і останній рядок (2150000000) обробляється COUNTоператором, що призводить до повідомлення про NULLповернення.

Для порівняння, замінюючи COUNTвираз SUM(CASE WHEN N < 2150000000 THEN 1 END)поверненнями

Msg 8115, рівень 16, стан 2, рядок 1
Арифметична помилка переповнення перетворення виразу в тип даних int.

без ANSIпопередження про NULL. Звідки я роблю висновок, що в цьому випадку переповнення сталося під час самої агрегації до досягнення рядів 2 150 000 000.


@PaulWhite - спасибі Я повинен був подивитися на XML. Я переглядав ScalarOperatorзначення, показане у вікні властивостей SSMS.
Мартін Сміт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.