2013-02-15 15 views
7

Mam tabelę z czymś danych jak poniżej:Wyniki Grupowanie SQL oparte na zamówienie

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 

chcę grupie każdy zestaw RowNumbers Tak, że mój wynik jest coś takiego:

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 

Jedyny sposób, w jaki wiem, gdzie każda grupa zaczyna się i kończy, kiedy RowNumber zaczyna od nowa. Jak mogę to zrobić? Musi też być dość efektywny, ponieważ tabela, w której muszę to zrobić, ma 52 miliony wierszy.

Dodatkowe informacje

ID jest naprawdę sekwencyjne, ale RowNumber może nie być. Myślę, że RowNumber zawsze zaczyna się od 1, ale na przykład RowNumbers dla grupy 1 może wynosić "1,1,2,2,3,4", a dla grupy 2 mogą być "1,2,4,6", itp.

+2

Jaką nazwę powinna otrzymać 27 grupa (po 'z')? –

+0

Co próbujesz znaleźć w zapytaniu? –

+0

@ypercube to nie ma znaczenia, o ile jego unikalna ... liczba zamiast liter byłaby w porządku. – matthew

Odpowiedz

6

dla sprecyzowanych wymagań w komentarzach

w rownumbers dla grupy1 może być „1,1,2,2,3,4” i grupy2 mogli być „1,2,4,6” ... im wyższa liczba, a następnie niższa, będzie nowa grupa .

Rozwiązanie SQL Server 2012 może wyglądać następująco.

  1. Zastosowanie LAG aby przejść do poprzedniego wiersza i ustaw flagę 1 jeśli rząd jest początek nowej grupy lub 0 inaczej.
  2. Oblicz bieżącą sumę tych flag, aby użyć ich jako wartości grupowania.

Kod

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

Zakładając ID jest skupione indeks plan ten ma jeden skan przed YourTable i unika jakichkolwiek operacji sortowania.

Plan

+0

Myślę, że RowNumber zawsze zaczyna się od 1, ale może nie być prawdziwie sekwencyjny. Na przykład. rownice dla grupy 1 mogą wynosić "1,1,2,2,3,4", a dla grupy 2 mogą być "1,2,4,6" – matthew

+0

@matthew - Więc co oznacza nowa grupa? Wyższa liczba, po której następuje niższa liczba? W jakiej wersji programu SQL Server jesteś? –

+0

Prawidłowo, wyższa liczba, a następnie niższa będzie nową grupą. – matthew

2

Jeśli identyfikatory są naprawdę sekwencyjne, można zrobić:

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

To wygląda obiecująco. Id jest naprawdę sekwencyjny, ale RowNumber może nie być. Na przykład. grupa1 mogłaby być równa "1,1,2,2,3,4", a grupa2 mogłaby być "1,2,4,6" – matthew

+0

@matthew - Jeśli "id" jest kolumną "TOŻSAMOŚCI", wówczas "prawdziwie sekwencyjny" nie jest gwarantowane. –

+0

@MartinSmith Wiem. Dodaję go do nagrań, więc mogę zapewnić, że tak naprawdę jest. – matthew

1

Również można użyć rekurencyjnej CTE

;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 na SQLFiddle

1

Jak o:

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 

To powinno działać na SQL 2005. Można również użyć funkcji rank(), jeśli nie interesują cię kolejne numery.