คุณสามารถใช้ COUNT DISTINCT กับ OVER clause ได้หรือไม่?


25

ฉันพยายามปรับปรุงประสิทธิภาพของข้อความค้นหาต่อไปนี้:

        UPDATE  [#TempTable]
        SET     Received = r.Number
        FROM    [#TempTable] 
        INNER JOIN (SELECT  AgentID,
                            RuleID,
                            COUNT(DISTINCT (GroupId)) Number
                    FROM    [#TempTable]
                    WHERE   Passed = 1
                    GROUP BY AgentID,
                            RuleID
                   ) r ON r.RuleID = [#TempTable].RuleID AND
                          r.AgentID = [#TempTable].AgentID                            

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

CREATE CLUSTERED INDEX ix_test ON #TempTable(AgentID, RuleId, GroupId, Passed)

และจริง ๆ แล้วมันเป็นสองเท่าของจำนวนเวลาที่แบบสอบถามใช้ ฉันได้รับผลกระทบเดียวกันกับดัชนีที่ไม่ใช่กลุ่ม

ฉันพยายามเขียนใหม่ดังนี้โดยไม่มีผลกระทบ

        WITH r AS (SELECT  AgentID,
                            RuleID,
                            COUNT(DISTINCT (GroupId)) Number
                    FROM    [#TempTable]
                    WHERE   Passed = 1
                    GROUP BY AgentID,
                            RuleID
            ) 
        UPDATE  [#TempTable]
        SET     Received = r.Number
        FROM    [#TempTable] 
        INNER JOIN r 
            ON r.RuleID = [#TempTable].RuleID AND
               r.AgentID = [#TempTable].AgentID                            

ต่อไปฉันพยายามใช้ฟังก์ชั่นหน้าต่างแบบนี้

        UPDATE  [#TempTable]
        SET     Received = COUNT(DISTINCT (CASE WHEN Passed=1 THEN GroupId ELSE NULL END)) 
                    OVER (PARTITION BY AgentId, RuleId)
        FROM    [#TempTable] 

ณ จุดนี้ฉันเริ่มได้รับข้อผิดพลาด

Msg 102, Level 15, State 1, Line 2
Incorrect syntax near 'distinct'.

ดังนั้นฉันมีสองคำถาม ก่อนอื่นคุณไม่สามารถทำการนับ COUNT DISTINCT ด้วย OVER clause หรือว่าฉันเพิ่งเขียนมันไม่ถูกต้อง? และทุกคนที่สองสามารถแนะนำการปรับปรุงที่ฉันยังไม่ได้ลองเลย FYI นี่เป็นอินสแตนซ์ของ SQL Server 2008 R2 Enterprise

แก้ไข: นี่คือลิงค์ไปยังแผนปฏิบัติการเดิม ฉันควรทราบด้วยว่าปัญหาใหญ่ของฉันคือแบบสอบถามนี้ถูกเรียกใช้ 30-50 ครั้ง

https://onedrive.live.com/redir?resid=4C359AF42063BD98%21772

แก้ไข 2: นี่คือวงเต็มที่คำสั่งอยู่ในตามที่ร้องขอในความคิดเห็น ฉันกำลังตรวจสอบกับคนที่ทำงานกับเรื่องนี้เป็นประจำเพื่อจุดประสงค์ของการวนซ้ำ

DECLARE @Counting INT              
SELECT  @Counting = 1              

--  BEGIN:  Cascading Rule check --           
WHILE @Counting <= 30              
    BEGIN      

        UPDATE  w1
        SET     Passed = 1
        FROM    [#TempTable] w1,
                [#TempTable] w3
        WHERE   w3.AgentID = w1.AgentID AND
                w3.RuleID = w1.CascadeRuleID AND
                w3.RulePassed = 1 AND
                w1.Passed = 0 AND
                w1.NotFlag = 0      

        UPDATE  w1
        SET     Passed = 1
        FROM    [#TempTable] w1,
                [#TempTable] w3
        WHERE   w3.AgentID = w1.AgentID AND
                w3.RuleID = w1.CascadeRuleID AND
                w3.RulePassed = 0 AND
                w1.Passed = 0 AND
                w1.NotFlag = 1        

        UPDATE  [#TempTable]
        SET     Received = r.Number
        FROM    [#TempTable] 
        INNER JOIN (SELECT  AgentID,
                            RuleID,
                            COUNT(DISTINCT (GroupID)) Number
                    FROM    [#TempTable]
                    WHERE   Passed = 1
                    GROUP BY AgentID,
                            RuleID
                   ) r ON r.RuleID = [#TempTable].RuleID AND
                          r.AgentID = [#TempTable].AgentID                            

        UPDATE  [#TempTable]
        SET     RulePassed = 1
        WHERE   TotalNeeded = Received              

        SELECT  @Counting = @Counting + 1              
    END

คำตอบ:


28

ปัจจุบันโครงสร้างนี้ไม่ได้รับการสนับสนุนใน SQL Server มันสามารถ (และในความคิดของฉัน) ควรจะดำเนินการในรุ่นอนาคต

การใช้หนึ่งในการแก้ไขปัญหาที่ระบุไว้ในรายการข้อเสนอแนะการรายงานข้อบกพร่องนี้แบบสอบถามของคุณสามารถเขียนใหม่เป็น:

WITH UpdateSet AS
(
    SELECT 
        AgentID, 
        RuleID, 
        Received, 
        Calc = SUM(CASE WHEN rn = 1 THEN 1 ELSE 0 END) OVER (
            PARTITION BY AgentID, RuleID) 
    FROM 
    (
        SELECT  
            AgentID,
            RuleID,
            Received,
            rn = ROW_NUMBER() OVER (
                PARTITION BY AgentID, RuleID, GroupID 
                ORDER BY GroupID)
        FROM    #TempTable
        WHERE   Passed = 1
    ) AS X
)
UPDATE UpdateSet
SET Received = Calc;

แผนการดำเนินการที่เกิดขึ้นคือ:

วางแผน

นี่เป็นข้อได้เปรียบของการหลีกเลี่ยง Eager Table Spool สำหรับHalloween Protection (เนื่องจากการรวมตัวเอง) แต่มันแนะนำการเรียงลำดับ (สำหรับหน้าต่าง) และ Lazy Table Spool ที่ไม่มีประสิทธิภาพในการคำนวณและใช้SUM OVER (PARTITION BY)ผลลัพธ์กับแถวทั้งหมด ในหน้าต่าง วิธีการปฏิบัติในทางปฏิบัติคือแบบฝึกหัดที่คุณทำได้เท่านั้น

วิธีการโดยรวมนั้นเป็นสิ่งที่ทำได้ยาก การใช้การอัปเดต (โดยเฉพาะอย่างยิ่งจากการเข้าร่วมด้วยตนเอง) ซ้ำกับโครงสร้างขนาดใหญ่อาจจะดีสำหรับการแก้ไขข้อบกพร่อง แต่มันเป็นสูตรสำหรับประสิทธิภาพที่ไม่ดี การสแกนขนาดใหญ่ซ้ำ ๆ การรั่วไหลของหน่วยความจำและปัญหาฮัลโลวีนเป็นเพียงปัญหาบางอย่าง การจัดทำดัชนีและ (เพิ่มเติม) ตารางชั่วคราวสามารถช่วยได้ แต่จำเป็นต้องมีการวิเคราะห์อย่างระมัดระวังโดยเฉพาะอย่างยิ่งหากดัชนีนั้นได้รับการปรับปรุงโดยคำสั่งอื่น ๆ ในกระบวนการ

ในที่สุดการแก้ปัญหาพื้นฐานจะทำให้งานให้คำปรึกษาที่น่าสนใจ แต่มันมากเกินไปสำหรับเว็บไซต์นี้ ฉันหวังว่าคำตอบนี้ตอบคำถามพื้นผิวว่า


การตีความทางเลือกของข้อความค้นหาต้นฉบับ (ผลลัพธ์ในการปรับปรุงแถวเพิ่มเติม):

WITH UpdateSet AS
(
    SELECT 
        AgentID, 
        RuleID, 
        Received, 
        Calc = SUM(CASE WHEN Passed = 1 AND rn = 1 THEN 1 ELSE 0 END) OVER (
            PARTITION BY AgentID, RuleID) 
    FROM 
    (
        SELECT  
            AgentID,
            RuleID,
            Received,
            Passed,
            rn = ROW_NUMBER() OVER (
                PARTITION BY AgentID, RuleID, Passed, GroupID
                ORDER BY GroupID)
        FROM    #TempTable
    ) AS X
)
UPDATE UpdateSet
SET Received = Calc
WHERE Calc > 0;

แผน 2

หมายเหตุ: การกำจัดการเรียงลำดับ (เช่นโดยการจัดทำดัชนี) อาจรื้อฟื้นความต้องการสำหรับ Eager Spool หรือสิ่งอื่น ๆ เพื่อให้การป้องกันฮาโลวีนที่จำเป็น การเรียงลำดับเป็นตัวดำเนินการบล็อกดังนั้นจึงมีการแยกเฟสแบบเต็ม


6

Necromancing:

เป็นเรื่องง่ายที่จะเลียนแบบการนับที่แตกต่างกันบนพาร์ติชันด้วย DENSE_RANK:

;WITH baseTable AS
(
              SELECT 'RM1' AS RM, 'ADR1' AS ADR
    UNION ALL SELECT 'RM1' AS RM, 'ADR1' AS ADR
    UNION ALL SELECT 'RM2' AS RM, 'ADR1' AS ADR
    UNION ALL SELECT 'RM2' AS RM, 'ADR2' AS ADR
    UNION ALL SELECT 'RM2' AS RM, 'ADR2' AS ADR
    UNION ALL SELECT 'RM2' AS RM, 'ADR3' AS ADR
    UNION ALL SELECT 'RM3' AS RM, 'ADR1' AS ADR
    UNION ALL SELECT 'RM2' AS RM, 'ADR1' AS ADR
    UNION ALL SELECT 'RM3' AS RM, 'ADR1' AS ADR
    UNION ALL SELECT 'RM3' AS RM, 'ADR2' AS ADR
)
,CTE AS
(
    SELECT RM, ADR, DENSE_RANK() OVER(PARTITION BY RM ORDER BY ADR) AS dr 
    FROM baseTable
)
SELECT
     RM
    ,ADR

    ,COUNT(CTE.ADR) OVER (PARTITION BY CTE.RM ORDER BY ADR) AS cnt1 
    ,COUNT(CTE.ADR) OVER (PARTITION BY CTE.RM) AS cnt2 
    -- Geht nicht / Doesn't work 
    --,COUNT(DISTINCT CTE.ADR) OVER (PARTITION BY CTE.RM ORDER BY CTE.ADR) AS cntDist
    ,MAX(CTE.dr) OVER (PARTITION BY CTE.RM ORDER BY CTE.RM) AS cntDistEmu 
FROM CTE

3
ซีแมนทิกส์ของสิ่งนี้ไม่เหมือนกับcountคอลัมน์ถ้าเป็นโมฆะ ถ้ามันมีโมฆะใด ๆ คุณต้องลบ 1
Martin Smith

@ มาร์ตินสมิ ธ : จับที่ดี เห็นได้ชัดว่าคุณต้องเพิ่ม WHERE ADR ที่ไม่เป็น NULL ถ้ามีค่า Null
แน่ใจ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.