2011-10-21 2 views
6

J'essaie de faire la tête parce que cela implique la comparaison de rangées consécutives. J'essaie de regrouper des valeurs qui diffèrent d'un certain nombre. Par exemple, laissez-nous dire que j'ai cette table:Obtenir toutes les lignes consécutives différant d'une certaine valeur?

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 

Et laissez-nous dire que je dois groupe toutes les valeurs qui diffèrent par 1 ayant la même valeur pour A. Je suis en train d'obtenir une sortie comme ceci:

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) et (8,3) (8,4) (8,5) (8,6) ont été mis dans le même groupe parce qu'ils diffèrent d'une valeur 1. Je vais d'abord montrer ma tentative:

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 

Je vais faire cela sur un grande table (environ 30 millions de lignes) donc j'espérais qu'il y aurait une meilleure façon de le faire pour des valeurs arbitraires (par exemple, non seulement différant de 1, mais cela pourrait être 2 ou 3 que j'incorporerai plus tard dans une procédure). Des suggestions pour savoir si mon approche est sans bug et si elle peut être améliorée?

Répondre

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 Merci pour votre temps! Cela fonctionne comme je le voulais. Je vais vérifier la performance sur celui-ci et revenir. Mon approche était presque similaire mais j'étais inquiet au sujet des jointures multiples. – Legend

3

Pour le cas où ils diffèrent par un, vous pouvez utiliser

;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 

Et plus généralement

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, il fonctionne parfaitement, réponse très bien pensé. – Lamak

+0

+1 Fonctionne parfaitement! Un éclaircissement rapide: Y a-t-il une façon naturelle d'étendre cela au cas où je considère que la différence est inférieure ou égale à la différence absolue? C'est-à-dire, dans le cas de '@ Interval = 2', il obtient également des valeurs qui diffèrent d'un dans le même groupe. Donc dans ce cas, il regroupe '(8,3) (8,4) (8,5) (8,6)' dans un groupe. – Legend

+0

@Legend - Il faudra penser à celui-là! Je pense que la réponse de Mikael était basée sur une hypothèse inférieure ou égale à l'hypothèse. –

Questions connexes