Tại sao máy chủ sql cần chuyển đổi kết quả đếm (*) thành int trước khi so sánh nó với biến int?


11

Tôi có nhiều truy vấn trong ứng dụng của mình trong đó trong mệnh đề có, tôi có so sánh hàm tổng hợp đếm với biến int. Trong các kế hoạch truy vấn, tôi có thể thấy một ẩn_convert trước khi so sánh. Tôi muốn biết lý do tại sao điều này xảy ra bởi vì theo tài liệu máy chủ sql, kiểu trả về của hàm đếm là int. Vậy tại sao nên có một chuyển đổi ngầm để so sánh hai giá trị int?

Sau đây là một phần của một kế hoạch truy vấn như vậy trong đó @IdCount được định nghĩa là biến int.

| - Bộ lọc (WHERE: ([Expr1022] = [@ IdCount]))    
 | - Tính toán vô hướng (DEFINE: ([Expr1022] = CONVERT_IMPLICIT (int, [Expr1028], 0))) 
  | - Tập hợp tổng hợp (NHÓM THEO: ([MOCK_DB]. [Dbo]. [Phạm vi]. [Phạm vi]] DEFINE: ([Expr1028] = Count (*)))

Câu trả lời:


17

Việc bạn so sánh nó với một integerbiến là không liên quan.

Kế hoạch cho COUNTluôn luôn có một CONVERT_IMPLICIT(int,[ExprNNNN],0))nơi ExprNNNNlà nhãn cho biểu đại diện cho các kết quả của sự COUNT.

Giả định của tôi luôn luôn là mã COUNTcuối cùng gọi cùng mã COUNT_BIGvà diễn viên là cần thiết để chuyển đổi bigintkết quả của mã đó trở lại int.

Trong thực tế COUNT_BIG(*)thậm chí không được phân biệt trong kế hoạch truy vấn từ COUNT(*). Cả hai hiện lên như Scalar Operator(Count(*)).

COUNT_BIG(nullable_column)không được phân biệt trong kế hoạch thực hiện từ COUNT(nullable_column) nhưng sau này vẫn nhận được một diễn viên ngầm quay trở lại int.

Một số bằng chứng cho thấy đây là trường hợp dưới đây.

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)

Điều này mất khoảng 7 phút để chạy trên máy tính để bàn của tôi và trả về như sau

Msg 8115, Cấp 16, Bang 2, Dòng 1
Lỗi tràn số học chuyển đổi biểu thức sang kiểu dữ liệu int.
Cảnh báo: Giá trị Null được loại bỏ bằng thao tác tổng hợp hoặc hoạt động SET khác.

Điều đó chỉ ra rằng COUNTphải tiếp tục sau khi intđã tràn (tại 2147483647) và hàng cuối cùng (2150000000) đã được xử lý bởi COUNTnhà điều hành dẫn đến thông báo về việc NULLđược trả lại.

Bằng cách so sánh thay thế COUNTbiểu thức bằng SUM(CASE WHEN N < 2150000000 THEN 1 END)trả về

Msg 8115, Cấp 16, Bang 2, Dòng 1
Lỗi tràn số học chuyển đổi biểu thức sang kiểu dữ liệu int.

không có ANSIcảnh báo về NULL. Từ đó tôi kết luận rằng sự cố tràn đã xảy ra trong trường hợp này trong quá trình tổng hợp trước khi đạt được hàng 2.150.000.000.


@PaulWhite - Cảm ơn. Tôi nên nhìn vào XML. Tôi đã xem xét ScalarOperatorgiá trị được hiển thị trong cửa sổ thuộc tính SSMS.
Martin Smith
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.