เหตุใด COUNT หลายรายการจึงเร็วกว่าหนึ่ง SUM ด้วย CASE


14

ฉันอยากรู้ว่าวิธีใดในสองวิธีต่อไปนี้เร็วกว่า:

1) สามCOUNT:

 SELECT Approved = (SELECT COUNT(*) FROM dbo.Claims d
                  WHERE d.Status = 'Approved'),
        Valid    = (SELECT COUNT(*) FROM dbo.Claims d
                    WHERE d.Status = 'Valid'),
        Reject   = (SELECT COUNT(*) FROM dbo.Claims d
                    WHERE d.Status = 'Reject')

2) SUMด้วยFROM--clause:

SELECT  Approved = SUM(CASE WHEN Status = 'Approved' THEN 1 ELSE 0 END),
        Valid    = SUM(CASE WHEN Status = 'Valid'    THEN 1 ELSE 0 END),
        Reject   = SUM(CASE WHEN Status = 'Reject'   THEN 1 ELSE 0 END)
FROM dbo.Claims c;

ฉันประหลาดใจว่าความแตกต่างนั้นใหญ่มาก แบบสอบถามแรกที่มีสามแบบสอบถามย่อยส่งคืนผลลัพธ์ทันทีในขณะที่SUMวิธีที่สองต้องการ 18 วินาที

Claimsคือมุมมองที่เลือกจากตารางที่มีประมาณ 18 ล้านแถว มีดัชนีในคอลัมน์ FK ไปยังClaimStatusตารางซึ่งมีชื่อสถานะอยู่

ทำไมมันถึงสร้างความแตกต่างอย่างมากไม่ว่าฉันจะใช้COUNTหรือSUM?

การดำเนินการแผน:

มีทั้งหมด 12 สถานะ สถานะทั้งสามนั้นเป็นของ 7% ของแถวทั้งหมด


นี่คือมุมมองจริงฉันไม่แน่ใจว่าเกี่ยวข้องหรือไม่:

CREATE VIEW [dbo].[Claims]
AS
SELECT 
   mu.Marketunitname AS MarketUnit, 
   c.Countryname     AS Country, 
   gsp.Gspname       AS GSP, 
   gsp.Wcmskeynumber AS GspNumber, 
   sl.Slname         AS SL, 
   sl.Wcmskeynumber  AS SlNumber, 
   m.Modelname       AS Model, 
   m.Salesname       AS [Model-Salesname], 
   s.Claimstatusname AS [Status], 
   d.Work_order      AS [Work Order], 
   d.Ssn_number      AS IMEI, 
   d.Ssn_out, 
   Remarks, 
   d.Claimnumber     AS [Claim-Number], 
   d.Rma_number      AS [RMA-Number], 
   dbo.ToShortDateString(d.Received_Date, 1) AS [Received Date], 
   Iddata, 
   Fisl, 
   Fimodel, 
   Ficlaimstatus 
FROM Tabdata AS d 
   INNER JOIN Locsl AS sl 
           ON d.Fisl = sl.Idsl 
   INNER JOIN Locgsp AS gsp 
           ON sl.Figsp = gsp.Idgsp 
   INNER JOIN Loccountry AS c 
           ON gsp.Ficountry = c.Idcountry 
   INNER JOIN Locmarketunit AS mu 
           ON c.Fimarketunit = mu.Idmarketunit 
   INNER JOIN Modmodel AS m 
           ON d.Fimodel = m.Idmodel 
   INNER JOIN Dimclaimstatus AS s 
           ON d.Ficlaimstatus = s.Idclaimstatus 
   INNER JOIN Tdefproducttype 
           ON d.Fiproducttype = Tdefproducttype.Idproducttype 
   LEFT OUTER JOIN Tdefservicelevel 
                ON d.Fimaxservicelevel = Tdefservicelevel.Idservicelevel 
   LEFT OUTER JOIN Tdefactioncode AS ac 
                ON d.Fimaxactioncode = ac.Idactioncode 

ดูเหมือนว่าลิงก์ทั้งสองจะชี้ไปที่COUNTเวอร์ชันของแผน คุณสามารถแก้ไขสิ่งที่คล้ายกับSUMเวอร์ชันเพื่อชี้ไปยังแผนที่ถูกต้องได้หรือไม่?
Geoff Patterson

อัตราส่วนของแถวที่มีสาม statii เทียบกับแถวกับ statii อื่น ๆ คืออะไร?
Max Vernon

1
@ MaxVernon: ใช่แน่นอนฉันเห็นค่าศูนย์มากเกินไปคุณพูดถูก ให้ฉันลบความคิดเห็นของฉัน ใช่มีสถานะอื่น 16.7 ล้านแถว ส่วนใหญ่Authorizedแล้ว
Tim Schmelter

2
ฉันจะประเมินแผนที่สองกำลังทุกข์ทรมานจากการต้องสแกนทั้งตาราง 12 ครั้ง (นั่นคือสิ่งที่แสดง) นี่เป็นโอกาสที่มาจากการไม่สามารถผลักภาคลงในการสแกน ประสิทธิภาพการทำงานเป็นอย่างไรถ้าคุณเพิ่มลงWHERE c.Status = 'Approved' or c.Status = 'Valid' or c.status = 'Reject'ในSUMตัวแปร
Max Vernon

@MaxVernon: มีทั้งหมดสิบสองสถานะ มันไม่ได้เป็นปัญหาสำหรับฉัน แต่ฉันประหลาดใจมากที่เครื่องมือเพิ่มประสิทธิภาพไม่สามารถจัดการกับสิ่งนี้ได้ ฉันควรทำงานทักษะการวิเคราะห์แผนปฏิบัติการของฉันจริงๆ ทำให้เป็นคำตอบ อะไรคือสมมติฐานของคุณทำไม SQL Server ถึงไม่สามารถสแกนสถานะได้เพียงสามสถานะเท่านั้น
Tim Schmelter

คำตอบ:


19

COUNT(*)รุ่นสามารถที่จะแสวงหาเพียงแค่เข้าไปในดัชนีที่คุณมีในคอลัมน์สถานะครั้งสำหรับแต่ละสถานะคุณเลือกในขณะที่SUM(...)ความต้องการของรุ่นที่จะแสวงหาดัชนีครั้งที่สิบสอง (จำนวนรวมของประเภทสถานะไม่ซ้ำกัน)

การค้นหาดัชนีอย่างชัดเจนสามครั้งจะเร็วกว่าการค้นหาดัชนี 12 ครั้ง

แผนแรกต้องการหน่วยความจำที่ให้สิทธิ์ 238MB ในขณะที่แผนสองต้องการหน่วยความจำ 650MB มันอาจเป็นไปได้ว่าทุนหน่วยความจำขนาดใหญ่ไม่สามารถที่เต็มไปทันทีทำให้แบบสอบถามที่ช้ามาก

เปลี่ยนเคียวรีที่สองเป็น:

SELECT  Approved = SUM(CASE WHEN Status = 'Approved' THEN 1 ELSE 0 END),
        Valid    = SUM(CASE WHEN Status = 'Valid'    THEN 1 ELSE 0 END),
        Reject   = SUM(CASE WHEN Status = 'Reject'   THEN 1 ELSE 0 END)
FROM dbo.Claims c
WHERE c.Status = 'Approved'
    OR c.Status = 'Valid'
    OR c.Status = 'Reject';

สิ่งนี้จะช่วยให้เคียวรีเครื่องมือเพิ่มประสิทธิภาพกำจัดการค้นหาดัชนี 75% และควรส่งผลให้หน่วยความจำที่ต้องการลดลงข้อกำหนดด้าน I / O ที่ลดลงและเวลาที่ได้ผลเร็วขึ้น

SUM(CASE WHEN ...)สร้างหลักป้องกันไม่ให้เพิ่มประสิทธิภาพการค้นหาจากการผลักดันStatusภาคลงไปในดัชนีขอเป็นส่วนหนึ่งของแผน


จับได้ดีกับหน่วยความจำ ฉันสังเกตเห็นว่า 32GB ทั้งหมดของฉันใช้งานอยู่ในปัจจุบัน (ฟรีเพียง 300 MB) แก้ไขอย่างไรก็ตามฉันได้เพิ่มหน่วยความจำบางส่วนแล้ว ผลลัพธ์จะเหมือนกัน
Tim Schmelter

คุณอาจต้องการดูmax server memoryตัวเลือก - ควรกำหนดค่าเป็นค่าที่ถูกต้องสำหรับระบบของคุณ คุณอาจต้องการดูคำถามนี้และคำตอบสำหรับรายละเอียดเกี่ยวกับวิธีการทำเช่นนั้น
Max Vernon

1
น่าเสียดายที่เซิร์ฟเวอร์นี้ไม่เพียง แต่ใช้สำหรับฐานข้อมูล แต่ยังสำหรับคิวบ์ SSAS และเครื่องมือบางอย่าง (รวมถึงเว็บแอปอินทราเน็ต) แต่ฉันได้กำหนดให้สูงสุด 12GB แล้ว
Tim Schmelter
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.