क्या आप एक खंड के साथ COUNT DISTINCT का उपयोग कर सकते हैं?


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'.

इसलिए मेरे दो सवाल हैं। पहले क्या आप OVER क्लॉज के साथ COUNT DISTINCT नहीं कर सकते या क्या मैंने इसे गलत तरीके से लिखा था? और दूसरा क्या कोई सुधार का सुझाव दे सकता है जो मैंने पहले ही नहीं आजमाया है? FYI करें यह SQL Server 2008 R2 एंटरप्राइज़ उदाहरण है।

EDIT: यहाँ मूल निष्पादन योजना की एक कड़ी है। मुझे यह भी ध्यान रखना चाहिए कि मेरी बड़ी समस्या यह है कि यह क्वेरी 30-50 बार चलाई जा रही है।

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

EDIT2: यहाँ पूर्ण लूप है कि कथन टिप्पणियों में अनुरोध के अनुसार है। मैं उस व्यक्ति के साथ जांच कर रहा हूं जो नियमित रूप से लूप के उद्देश्य के साथ काम करता है।

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 सर्वर में समर्थित नहीं है। यह (और, मेरी राय में) भविष्य के संस्करण में लागू किया जा सकता है।

इस कमी की रिपोर्ट करने वाले फ़ीडबैक आइटम में सूचीबद्ध कार्यपत्रों में से एक को लागू करते हुए, आपकी क्वेरी को फिर से लिखा जा सकता है:

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;

परिणामी निष्पादन योजना है:

योजना

हैलोवीन प्रोटेक्शन (सेल्फ-जॉइन के कारण) के लिए एगर टेबल स्पूल से बचने का यह फायदा है , लेकिन यह SUM OVER (PARTITION BY)सभी पंक्तियों के परिणाम की गणना करने और उन्हें लागू करने के लिए एक प्रकार (अकुशल आलसी टेबल स्पूल निर्माण) करता है। खिड़की में। यह अभ्यास में कैसा प्रदर्शन करता है यह केवल एक अभ्यास है जिसे आप कर सकते हैं।

समग्र दृष्टिकोण अच्छा प्रदर्शन करने के लिए एक कठिन है। अद्यतनों को लागू करना (विशेष रूप से स्वयं पर आधारित) एक बड़ी संरचना के लिए पुनरावर्ती रूप से डिबगिंग के लिए अच्छा हो सकता है, लेकिन यह खराब प्रदर्शन के लिए एक नुस्खा है। बार-बार बड़े स्कैन, मेमोरी स्पिल और हैलोवीन इश्यू कुछ ही मुद्दे हैं। अनुक्रमण और (अधिक) अस्थायी तालिकाओं में मदद मिल सकती है, लेकिन बहुत सावधानीपूर्वक विश्लेषण की आवश्यकता होती है, खासकर यदि अनुक्रमणिका प्रक्रिया में अन्य बयानों द्वारा अद्यतन की जाती है (अनुक्रमणिका क्वेरी योजना विकल्पों को प्रभावित करती है और I / O जोड़ता है)।

अंततः, अंतर्निहित समस्या को हल करना दिलचस्प परामर्श कार्य के लिए करना होगा, लेकिन यह इस साइट के लिए बहुत अधिक है। मुझे उम्मीद है कि यह उत्तर सतह के प्रश्न को संबोधित करेगा।


मूल क्वेरी की वैकल्पिक व्याख्या (अधिक पंक्तियों को अपडेट करने के परिणाम):

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;

योजना २

नोट: सॉर्ट को खत्म करना (जैसे कि एक इंडेक्स प्रदान करके) ईगर स्पूल की आवश्यकता को फिर से लागू कर सकता है या आवश्यक हेलोवीन सुरक्षा प्रदान करने के लिए कुछ और। सॉर्ट एक अवरुद्ध ऑपरेटर है, इसलिए यह पूर्ण चरण पृथक्करण प्रदान करता है।


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. घटाना होगा
मार्टिन स्मिथ

@ मर्टिन स्मिथ: अच्छा कैच। जाहिर है कि अगर आपको अशक्त-मूल्य हैं, तो आपको ADR नहीं जोड़ना होगा।
Quandary
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.