2010-09-29 2 views
9

Ich habe mit Sätzen in SQL Server 2000 zu spielen und haben die folgende Tabellenstruktur für einen meiner Temptabellen (#Periods):Auswahl SUM von TOP 2 Werte innerhalb einer Tabelle mit mehreren GROUP in SQL

 
    RestCTR  HoursCTR Duration Rest 
    ---------------------------------------- 
    1   337   2   0 
    2   337   46   1 
    3   337   2   0 
    4   337   46   1 
    5   338   1   0 
    6   338   46   1 
    7   338   2   0 
    8   338   46   1 
    9   338   1   0 
    10   339   46   1 
    ... 

Was ich tun möchte, ist die Berechnung der Summe der 2 längsten Ruhezeiten für jede StundeCTR, vorzugsweise mit Sets und Tabellen (anstatt Cursor oder verschachtelte Unterabfragen).

Hier ist der Traum Abfrage, die einfach nicht in SQL arbeiten (egal wie oft ich es laufen):

Select HoursCTR, SUM (TOP 2 Duration) as LongestBreaks 
FROM #Periods 
WHERE Rest = 1 
Group By HoursCTR  

Die HoursCTR eine beliebige Anzahl von Ruhezeiten haben können (einschließlich keine).

Meine aktuelle Lösung ist nicht sehr elegant und umfasst im Wesentlichen die folgenden Schritte:

  1. die maximale Dauer des Restes holt Gruppe von HoursCTR
  2. Wählen Sie die erste (min) RestCTR Zeile, die diesen max Dauer zurückzugibt für jeden HoursCTR
  3. Wiederholen Sie Schritt 1 (mit Ausnahme der Zeilen bereits in Schritt 2 gesammelt)
  4. Wiederholen Sie Schritt 2 (auch hier ohne Zeilen in Schritt gesammelt 2)
  5. Kombinieren des RestCTR Zeilen (aus Schritt 2 und 4) in einzelne Tabelle
  6. Get Summe der Dauer bis 5 durch die Zeilen in Schritt darauf, gruppiert nach HoursCTR

Wenn es irgendwelche eingestellten Funktionen sind, die diesen Prozess nach unten geschnitten, sie wäre sehr willkommen.

Antwort

7

Der beste Weg, dies in SQL Server zu tun ist, mit einem common table expression, die Zeilen in jeder Gruppe mit der Funktion Windowing Nummerierung ROW_NUMBER():

WITH NumberedPeriods AS (
    SELECT HoursCTR, Duration, ROW_NUMBER() 
    OVER (PARTITION BY HoursCTR ORDER BY Duration DESC) AS RN 
    FROM #Periods 
    WHERE Rest = 1 
) 
SELECT HoursCTR, SUM(Duration) AS LongestBreaks 
FROM NumberedPeriods 
WHERE RN <= 2 
GROUP BY HoursCTR 

edit: Ich habe eine ORDER BY-Klausel hinzugefügt die Partitionierung, um die zwei längsten Pausen zu erhalten.


Mea culpa, habe ich nicht feststellen, dass Sie dies benötigen in Microsoft SQL Server arbeiten 2000. Diese Version unterstützt keine CTE oder Windowing-Funktionen. Ich lasse die Antwort oben, falls es jemand anderem hilft.

In SQL Server 2000, die gemeinsame Beratung ist eine korrelierte Unterabfrage zu verwenden:

SELECT p1.HoursCTR, (SELECT SUM(t.Duration) FROM 
    (SELECT TOP 2 p2.Duration FROM #Periods AS p2 
    WHERE p2.HoursCTR = p1.HoursCTR 
    ORDER BY p2.Duration DESC) AS t) AS LongestBreaks 
FROM #Periods AS p1 
+0

Wie wählt es zwei _longest_ Pausen Was bin ich? – Arkadiy

+2

OP angegeben SQL Server 2000. ROW_NUMBER() und CTE sind nicht verfügbar. – bobs

+0

@bobs: Danke, das habe ich verpasst. Ich habe eine andere Lösung hinzugefügt. –

1

Leider für Sie, Alex, haben Sie die richtige Lösung bekam: korrelierte Unterabfragen, je nachdem, wie sie strukturiert sind , wird am Ende mehrere Male feuern, wodurch Sie möglicherweise Hunderte von einzelnen Abfrageausführungen erhalten.

Setzen Sie Ihre aktuelle Lösung in den Query Analyzer, aktivieren Sie "Ausführungsplan anzeigen" (Strg + K) und führen Sie sie aus. Am unteren Rand sehen Sie einen zusätzlichen Tab, der Ihnen zeigt, wie der Motor beim Sammeln der Ergebnisse vorging. Wenn Sie das Gleiche mit der korrelierten Unterabfrage tun, werden Sie sehen, was diese Option tut.

Ich glaube, dass es wahrscheinlich ist, die Tabelle #Periods so oft zu hämmern, wie Sie einzelne Zeilen in dieser Tabelle haben.

Auch - etwas ist los über die korrelierte Unterabfrage, scheint mir. Da ich sie wie die Pest meide, weil ich weiß, dass sie böse sind, bin ich mir nicht sicher, wie ich es reparieren soll.

+0

Dank David, ich hatte gehofft, jemand könnte von cleveren Tricks wissen, die ich nicht entdeckt hatte, aber es sieht so aus, als ob dieser 6-Schritte-Prozess das Ende der Linie sein könnte. –

+0

Nun ... Sie sagen der Datenbank-Engine, was zu tun ist und wie es zu tun ist, was wirklich keine schlechte Sache ist. Der Motor ist nicht so hell, wenn es darauf ankommt. Wie sollte es wissen, dass Sie nicht wollen, dass es zu iterativem Müll wird, wenn Sie es dazu sagen? –

2

SQL 2000 hat keine CTEs und keine ROW_NUMBER().
Korrelierte Unterabfragen können bei Verwendung von group by einen zusätzlichen Schritt erfordern.

Dies sollte für Sie arbeiten:

SELECT 
    F.HoursCTR, 
    MAX (F.LongestBreaks) AS LongestBreaks -- Dummy max() so that groupby can be used. 
FROM 
    (
     SELECT 
      Pm.HoursCTR, 
      (
       SELECT 
        COALESCE (SUM (S.Duration), 0)  
       FROM 
        (
         SELECT TOP 2 T.Duration 
         FROM   #Periods AS T 
         WHERE   T.HoursCTR = Pm.HoursCTR 
         AND    T.Rest  = 1 
         ORDER BY  T.Duration DESC 
        ) AS S 
      ) AS LongestBreaks 
     FROM 
      #Periods AS Pm 
    ) AS F 
GROUP BY 
    F.HoursCTR 
Verwandte Themen