ทำไมเซิร์ฟเวอร์ sql จำเป็นต้องแปลงผลลัพธ์ count (*) เป็น int ก่อนที่จะเปรียบเทียบกับตัวแปร int?


11

ฉันมีคำถามมากมายในแอปพลิเคชันของฉันซึ่งในประโยคที่มีฉันมีการเปรียบเทียบฟังก์ชั่นการนับรวมกับตัวแปร int ในแผนคิวรีฉันสามารถเห็น implicit_convert ก่อนการเปรียบเทียบ ฉันต้องการทราบว่าทำไมสิ่งนี้เกิดขึ้นเพราะตามเอกสารเซิร์ฟเวอร์ sql ฟังก์ชั่นการนับกลับเป็น int เหตุใดจึงต้องมีการแปลงโดยนัยสำหรับการเปรียบเทียบค่า int สองค่า

ต่อไปนี้เป็นส่วนหนึ่งของแผนแบบสอบถามหนึ่งที่ @IdCount ถูกกำหนดให้เป็นตัวแปร int

| --Filter (สถานที่: ([Expr1022] = [@ IdCount]))    
 | - คำนวณสเกลาร์ (คำจำกัดความ: ([Expr1022] = CONVERT_IMPLICIT (int, [Expr1028], 0))) 
  | - สตรีมรวม (กลุ่มตาม: ([MOCK_DB]. [dbo]. [ขอบเขต]. [ขอบเขต ID]) กำหนด: ([Expr1028] = จำนวน (*)))

คำตอบ:


17

ความจริงที่ว่าคุณกำลังเปรียบเทียบกับintegerตัวแปรนั้นไม่เกี่ยวข้อง

แผนCOUNTเสมอมีCONVERT_IMPLICIT(int,[ExprNNNN],0))ที่เป็นฉลากสำหรับการแสดงออกที่เป็นตัวแทนผลมาจากการที่ExprNNNNCOUNT

สมมติฐานของฉันได้รับเสมอว่ารหัสสำหรับCOUNTเพียงปลายขึ้นเรียกรหัสเดียวกับCOUNT_BIGและนักแสดงเป็นสิ่งที่จำเป็นในการแปลงผลจากการที่ลงกลับไปbigintint

ในความเป็นจริงจะไม่โดดเด่นแม้ในแผนแบบสอบถามจาก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 นาทีในการรันบนเดสก์ท็อปของฉันและส่งคืนสิ่งต่อไปนี้

ข่าวสารเกี่ยวกับ 8115 ระดับ 16 สถานะ 2 บรรทัด 1
ข้อผิดพลาดโอเวอร์โฟนิกเลขคณิตแปลงนิพจน์เป็นประเภทข้อมูล int
คำเตือน: ค่า Null จะถูกกำจัดโดยการรวมหรือการดำเนินการของ SET อื่น ๆ

ซึ่งบ่งชี้ว่าCOUNTต้องดำเนินการต่อหลังจากintมีการล้น (ที่ 2147483647) และแถวสุดท้าย (2150000000) ได้รับการดำเนินการโดยCOUNTผู้ดำเนินการที่นำไปสู่ข้อความเกี่ยวกับNULLการส่งคืน

โดยวิธีการเปรียบเทียบแทนที่การCOUNTแสดงออกด้วยSUM(CASE WHEN N < 2150000000 THEN 1 END)ผลตอบแทน

ข่าวสารเกี่ยวกับ 8115 ระดับ 16 สถานะ 2 บรรทัด 1
ข้อผิดพลาดโอเวอร์โฟนิกเลขคณิตแปลงนิพจน์เป็นประเภทข้อมูล int

ไม่มีคำเตือนเกี่ยวกับANSI NULLจากที่ฉันสรุปล้นที่เกิดขึ้นในกรณีนี้ในระหว่างการรวมตัวเองก่อนถึง 2,150,000,000 แถว


@PaulWhite - ขอบคุณ ฉันควรดู XML ฉันดูScalarOperatorค่าที่แสดงในหน้าต่างคุณสมบัติ SSMS
Martin Smith
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.