2013-02-15 14 views
7

ho tavolo con qualcosa di dati come questo:Risultati Raggruppamento SQL basate su ordine

ID  | RowNumber  | Data 
------------------------------ 
1  | 1    | Data 
2  | 2    | Data 
3  | 3    | Data 
4  | 1    | Data 
5  | 2    | Data 
6  | 1    | Data 
7  | 2    | Data 
8  | 3    | Data 
9  | 4    | Data 

Voglio gruppo ogni set di RowNumbers In modo che il mio risultato è qualcosa di simile:

ID  | RowNumber  | Group | Data 
-------------------------------------- 
1  | 1    | a  | Data 
2  | 2    | a  | Data 
3  | 3    | a  | Data 
4  | 1    | b  | Data 
5  | 2    | b  | Data 
6  | 1    | c  | Data 
7  | 2    | c  | Data 
8  | 3    | c  | Data 
9  | 4    | c  | Data 

Il solo il modo in cui so dove inizia e si ferma ogni gruppo è quando il RowNumber ricomincia. Come posso realizzare questo? Deve anche essere abbastanza efficiente poiché il tavolo su cui ho bisogno di fare questo ha 52 milioni di righe.

Altre Informazioni

ID è veramente sequenziale, ma RowNumber non può essere. Penso che RowNumber inizierà sempre con 1 ma ad esempio RowNumbers per group1 potrebbe essere "1,1,2,2,3,4" e per group2 potrebbero essere "1,2,4,6", ecc.

+2

Quale nome dovrebbe ricevere il 27 ° gruppo (dopo 'z')? –

+0

Cosa stai cercando di trovare dalla query? –

+0

@ypercube non importa solo se il suo numero univoco ... al posto delle lettere va bene. – matthew

risposta

6

per i requisiti chiarite nei commenti

le rownumbers per gruppo 1 potrebbe essere "1,1,2,2,3,4" e per group2 che potrebbero essere "1,2,4,6" ... un numero più alto seguito da un valore inferiore sarebbe un nuovo gruppo .

Una soluzione di SQL Server 2012 potrebbe essere la seguente.

  1. Usa LAG per accedere alla riga precedente e impostare un flag per 1 se tale riga è l'inizio di un nuovo gruppo o 0 altrimenti.
  2. Calcolare una somma parziale di questi flag da utilizzare come valore di raggruppamento.

Codice

WITH T1 AS 
(
SELECT *, 
     LAG(RowNumber) OVER (ORDER BY ID) AS PrevRowNumber 
FROM YourTable 
), T2 AS 
(
SELECT *, 
     IIF(PrevRowNumber IS NULL OR PrevRowNumber > RowNumber, 1, 0) AS NewGroup 
FROM T1 
) 
SELECT ID, 
     RowNumber, 
     Data, 
     SUM(NewGroup) OVER (ORDER BY ID 
          ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS Grp 
FROM T2 

SQL Fiddle

Supponendo ID è l'indice cluster il piano per questo ha una scansione contro YourTable ed evita eventuali operazioni di ordinamento.

Plan

+0

Penso che RowNumber inizierà sempre con 1, ma potrebbe non essere veramente sequenziale. Per esempio. i rumbetri per il gruppo1 potrebbero essere "1,1,2,2,3,4" e per il gruppo2 potrebbero essere "1,2,4,6" – matthew

+0

@matthew - Quindi cosa significa un nuovo gruppo? Un numero più alto seguito da un numero più basso? In quale versione di SQL Server ci si trova? –

+0

Corretto, un numero più alto seguito da un valore più basso sarebbe un nuovo gruppo. – matthew

2

Se gli ID sono davvero sequenziale, si può fare:

select t.*, 
     (id - rowNumber) as grp 
from t 
+0

Sembra promettente. L'ID è veramente sequenziale, ma RowNumber potrebbe non esserlo. Per esempio. group1 potrebbe essere uguale a "1,1,2,2,3,4" e group2 potrebbe essere "1,2,4,6" – matthew

+0

@matthew - Se 'id' è una colonna' IDENTITY' quindi "veramente sequenziale" non è garantita. –

+0

@MartinSmith lo so. Lo aggiungo ai record, quindi posso assicurarmi che sia davvero così. – matthew

1

Inoltre è possibile utilizzare CTE ricorsiva

;WITH cte AS 
(  
    SELECT ID, RowNumber, Data, 1 AS [Group] 
    FROM dbo.test1 
    WHERE ID = 1 
    UNION ALL 
    SELECT t.ID, t.RowNumber, t.Data, 
     CASE WHEN t.RowNumber != 1 THEN c.[Group] ELSE c.[Group] + 1 END 
    FROM dbo.test1 t JOIN cte c ON t.ID = c.ID + 1 
) 
    SELECT * 
    FROM cte 

Demo SQLFiddle

1

ne dite di:

select ID, RowNumber, Data, dense_rank() over (order by grp) as Grp 
from (
    select *, (select min(ID) from [Your Table] where ID > t.ID and RowNumber = 1) as grp 
    from [Your Table] t 
) t 
order by ID 

Questo dovrebbe funzionare su SQL 2005. È possibile utilizzare anche rank() se non si cura dei numeri consecutivi.