2010-09-29 12 views
9

Ho giocato con i set in SQL Server 2000 e avere la seguente struttura della tabella per una delle mie tabelle temporanee (#Periods):Selezione SUM di TOP 2 valori all'interno di una tabella con GROUP multiplo 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 
    ... 

Quello che mi piacerebbe fare è calcolare la somma dei 2 periodi di riposo più lunghi per ogni HoursCTR, preferibilmente usando insiemi e tabelle temporanee (piuttosto che cursori o subquery annidate).

Ecco la query sogno che semplicemente non funziona in SQL (non importa quante volte lo faccio funzionare):

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

Il HoursCTR può avere qualsiasi numero di periodi di riposo (tra cui nessuno).

La mia soluzione attuale non è molto elegante e comporta fondamentalmente le seguenti fasi:

  1. Prendi la durata massima di riposo, gruppo da HoursCTR
  2. Selezionare il primo (min) RestCTR riga che restituisce questo durata max per ogni HoursCTR
  3. Ripetere il passaggio 1 (escluse le file già raccolti nel passaggio 2)
  4. Ripetere passaggio 2 (sempre escludendo righe raccolti nel passaggio 2)
  5. Combinare il RestCTR righe (dal punto 2 e 4) in un'unica tabella
  6. Get somma della durata puntato dalla righe nel passaggio 5, raggruppati per HoursCTR

Se ci sono delle funzioni impostate che tagliano questo processo giù, sarebbe molto gradito

risposta

7

Il modo migliore per farlo in SQL Server è con una common table expression, la numerazione delle righe in ogni gruppo con la funzione di windowing 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: Ho aggiunto una clausola ORDER BY in il partizionamento, per ottenere i due resti più lunghi.


Mea culpa, non ho notato che avete bisogno di questo per lavorare in Microsoft SQL Server 2000. Questa versione non supporta funzioni di windowing CTE del o. Lascerò la risposta sopra nel caso in cui aiuti qualcun altro.

In SQL Server 2000, il consiglio comune è quello di utilizzare una subquery correlata:

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

Come ci si selezionano due pause _longest_ Che cosa mi manca? – Arkadiy

+2

OP indica SQL Server 2000. ROW_NUMBER() e CTE non sono disponibili. – bobs

+0

@bobs: Grazie, mi sono perso. Ho aggiunto una soluzione diversa. –

1

Purtroppo per voi, Alex, hai la soluzione giusta: Sottointerrogazioni correlate, a seconda di come sono strutturati , finirà per sparare più volte, potenzialmente dando centinaia di singole esecuzioni di query.

Inserire la soluzione corrente nell'analizzatore di query, attivare "Mostra piano di esecuzione" (Ctrl + K) ed eseguirlo. Nella parte inferiore troverai una scheda aggiuntiva che ti mostrerà come è andato il motore per il processo di raccolta dei risultati. Se fai lo stesso con la sottoquery correlata, vedrai cosa fa questa opzione.

Credo che sia probabile che sia possibile martellare la tabella #Periods quante volte quante sono le singole righe in quella tabella.

Inoltre, mi sembra che qualcosa stia parlando della subquery correlata. Dal momento che li evito come la peste, sapendo che sono cattivi, non sono sicuro di come procedere per sistemarlo.

+0

Grazie David, speravo che qualcuno potesse sapere di trucchi intelligenti che non avevo scoperto, ma sembra che questo processo in 6 passaggi potrebbe essere la fine della linea. –

+0

Beh ... stai dicendo al motore del database cosa fare e come farlo, il che non è poi così male. Il motore non è così brillante, quando si tratta di esso. Come dovrebbe sapere che non vuoi che sia spazzatura iterativa se gli dici di farlo? –

2

SQL 2000 non ha CTE, né ROW_NUMBER().
Le sottoquery correlate possono richiedere un passaggio aggiuntivo quando si utilizza group by.

Questo dovrebbe funzionare per voi:

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 
Problemi correlati