COUNT DISTINCT'i bir OVER deyimiyle kullanabilir misiniz?


25

Aşağıdaki sorgunun performansını artırmaya çalışıyorum:

        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                            

Şu anda test verilerim ile yaklaşık bir dakika sürer. Bu sorgunun bulunduğu tüm saklı yordamda değişiklik yapma konusunda sınırlı miktarda girdi var, ancak büyük olasılıkla bu sorguyu değiştirmelerini sağlayabilirim. Veya bir dizin ekleyin. Aşağıdaki dizini eklemeye çalıştım:

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

Ve aslında sorgunun harcadığı zamanı iki katına çıkardı. Aynı etkiyi CLUSTERED endeksiyle elde ediyorum.

Etkisi olmayan şekilde tekrar yazmaya çalıştım.

        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                            

Sonra böyle bir pencere işlevi kullanmaya çalıştım.

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

Bu noktada hatayı almaya başladım

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

Bu yüzden iki sorum var. Öncelikle, OVER yan tümcesinde COUNT DISTINCT yapamazsınız veya yanlış mı yazdım? İkincisi, daha önce denemediğim bir gelişme önerebilir mi? FYI bu bir SQL Server 2008 R2 Enterprise örneğidir.

EDIT: İşte orijinal uygulama planına bir link. Ayrıca benim büyük sorunumun bu sorgunun 30-50 kez çalıştırılması olduğunu da belirtmeliyim.

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

EDIT2: İşte açıklamalarda istendiği gibi ifadenin tam döngüsü. Döngü amacına göre bununla düzenli olarak çalışan kişiyi kontrol ediyorum.

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

Yanıtlar:


28

Bu yapı şu anda SQL Server'da desteklenmiyor. Gelecekteki bir versiyonda uygulanabilir (ve bence).

Bu eksikliği bildiren geri bildirim öğesinde listelenen geçici çözümlerden birini kullanarak, sorgunuz şu şekilde yeniden yazılabilir:

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;

Ortaya çıkan yürütme planı:

Plan

Bu, Cadılar Bayramı Koruması için İstekli Bir Tablo Makarası'ndan kaçınma avantajına sahiptir (kendi kendine katılma nedeniyle), ancak SUM OVER (PARTITION BY)sonucu tüm satırlara hesaplamak ve uygulamak için bir sıralama (pencere için) ve genellikle yetersiz olan bir Tembel Tablo Makarası yapısı sunar. pencerede. Uygulamada nasıl performans gösterdiğini sadece sizin gerçekleştirebileceğiniz bir egzersiz.

Genel yaklaşım, iyi performans göstermesi zor bir yaklaşımdır. Güncellemeleri (özellikle kendi kendine birleşmeye dayalı olanları) tekrar tekrar büyük bir yapıya uygulamak, hata ayıklama için iyi olabilir, ancak düşük performans için bir reçetedir. Tekrarlanan büyük taramalar, bellek sızıntıları ve Cadılar Bayramı sorunları, sorunlardan yalnızca birkaçıdır. Dizin oluşturma ve (daha fazla) geçici tablolar yardımcı olabilir, ancak özellikle dizin işlemdeki diğer ifadelerle güncellenirse (dizinleri korumak sorgu planı seçimlerini etkiler ve G / Ç ekler) çok dikkatli analiz gerekir.

Sonuçta, altta yatan problemin çözülmesi ilginç danışmanlık çalışması için faydalı olacaktır, ancak bu site için çok fazla. Umarım bu cevap olsa yüzey sorusunu ele almaktadır.


Orijinal sorgunun alternatif yorumu (daha fazla satırın güncellenmesiyle sonuçlanır):

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;

Plan 2

Not: Sıralamayı kaldırmak (örneğin, bir indeks sağlayarak), gerekli Cadılar Bayramı Korumasını sağlamak için Eager Biriktirme ihtiyacını veya başka bir şeyi tekrar ortaya koyabilir. Sort, bir engelleme operatörü olduğundan tam faz ayrımı sağlar.


6

Necromancing:

DENSE_RANK ile bölümlere göre farklı bir sayım taklit etmek oldukça kolaydır:

;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
Bunun anlambilgisi count, sütun null değerindeyse aynı değildir . Herhangi bir boş değer içeriyorsa, 1'i çıkarmanız gerekir.
Martin Smith

@ Martin Smith: Güzel yakalamak. Açıkçası, null-değerler varsa NEREDE ADR OLMADIĞINI NULL eklemeniz gerekir.
Quandary
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.