2011-10-21 18 views
6

Sto provando a capirlo facendo questo perché comporta il confronto di righe consecutive. Sto cercando di raggruppare valori che differiscono di un certo numero. Per esempio, diciamo che ho questa tabella:Tutte le righe consecutive differiscono di un determinato valore?

CREATE TABLE #TEMP (A int, B int) 

-- Sample table 
INSERT INTO #TEMP VALUES 
(3,1), 
(3,2), 
(3,3), 
(3,4), 
(5,1), 
(6,1), 
(7,2), 
(8,3), 
(8,4), 
(8,5), 
(8,6) 

SELECT * FROM #TEMP 

DROP TABLE #TEMP 

E diciamo che devo gruppo tutti i valori che differiscono di 1 aventi lo stesso valore per A. Poi sto cercando di ottenere un output simile a questo:

A B GroupNo 
3 1 1 
3 2 1 
3 3 1 
3 4 1 
5 1 2 
6 1 3 
7 2 4 
8 3 5 
8 4 5 
8 5 5 
8 6 5 

(3,1) (3,2) (3,3) (3,4) e (8,3) (8,4) (8,5) (8,6) sono stati messi nello stesso gruppo perché essi differiscono da un valore di 1. voglio prima vedere il mio tentativo:

CREATE TABLE #TEMP (A int, B int) 

-- Sample table 
INSERT INTO #TEMP VALUES 
(3,1), (3,2), (3,3), (3,4), (5,1), (6,1), (7,2), 
(8,3), (8,4), (8,5), (8,6) 

-- Assign row numbers and perform a left join 
-- so that we can compare consecutive rows 
SELECT ROW_NUMBER() OVER (ORDER BY A ASC) ID, * 
INTO #TEMP2 
FROM #TEMP 

;WITH CTE AS 
(
    SELECT X.A XA, X.B XB, Y.A YA, Y.B YB 
    FROM #TEMP2 X 
    LEFT JOIN #TEMP2 Y 
    ON X.ID = Y.ID - 1 
    WHERE X.A = Y.A AND 
    X.B = Y.B - 1 
) 
SELECT XA, XB 
INTO #GROUPS 
FROM CTE 
UNION 
SELECT YA, YB 
FROM CTE 
ORDER BY XA ASC 

-- Finally assign group numbers 
SELECT X.XA, X.XB, Y.GID 
FROM #GROUPS X 
INNER JOIN 
(SELECT XA, ROW_NUMBER() OVER (ORDER BY XA ASC) GID 
    FROM #GROUPS Y 
    GROUP BY XA 
) Y 
ON X.XA = Y.XA 

DROP TABLE #TEMP 
DROP TABLE #TEMP2 
DROP TABLE #GROUPS 

che cercherò di fare questo su un grande tavolo (circa 30 milioni di righe) quindi speravo che ci fosse un modo migliore per farlo per valori arbitrari (per esempio, non solo differendo di 1, ma potrebbe essere 2 o 3 che incorporerò più avanti in una procedura). Qualche suggerimento sul fatto che il mio approccio sia privo di bug e se possa essere migliorato?

risposta

2
declare @Diff int = 1 

;with C as 
(
    select A, 
     B, 
     row_number() over(partition by A order by B) as rn 
    from #TEMP 
), 
R as 
(
    select C.A, 
     C.B, 
     1 as G, 
     C.rn 
    from C 
    where C.rn = 1 
    union all 
    select C.A, 
     C.B, 
     G + case when C.B-R.B <= @Diff 
       then 0 
       else 1 
      end, 
     C.rn 
    from C 
    inner join R 
     on R.rn + 1 = C.rn and 
      R.A = C.A  
) 
select A, 
     B, 
     dense_rank() over(order by A, G) as G 
from R 
order by A, G 
+0

+1 Grazie per il tuo tempo! Funziona come volevo. Controllerò le prestazioni su questo e tornerò. Il mio approccio era quasi simile ma ero preoccupato per i molteplici join. – Legend

3

Per il caso in cui differiscono da quello che si può utilizzare

;WITH T AS 
(
SELECT *, 
     B - DENSE_RANK() OVER (PARTITION BY A ORDER BY B) AS Grp 
FROM #TEMP 
) 
SELECT A, 
     B, 
     DENSE_RANK() OVER (ORDER BY A,Grp) AS GroupNo 
FROM T 
ORDER BY A, Grp 

E più in generale

DECLARE @Interval INT = 2 

;WITH T AS 
(
SELECT *, 
     B/@Interval - DENSE_RANK() OVER (PARTITION BY A, B%@Interval ORDER BY B) AS Grp 
FROM #TEMP 
) 
SELECT A, 
     B, 
     DENSE_RANK() OVER (ORDER BY A, B%@Interval,Grp) AS GroupNo 
FROM T 
ORDER BY A, GroupNo 
+0

+1, funziona perfettamente, risposta molto ben pensato. – Lamak

+0

+1 Funziona perfettamente! Un rapido chiarimento: esiste un modo naturale per estendere questo al caso in cui io sto guardando meno o uguale al posto della differenza assoluta? Cioè, nel caso di '@ Intervallo = 2', ottiene anche valori che differiscono di uno nello stesso gruppo. Quindi, in questo caso, raggruppa '(8,3) (8,4) (8,5) (8,6)' in un gruppo. – Legend

+0

@Legend - Dovrà pensarci! Penso che la risposta di Mikael fosse basata sul meno o uguale all'assunzione. –

Problemi correlati