2011-10-21 12 views
6

Estoy tratando de entender esto ya que implica la comparación de filas consecutivas. Estoy tratando de agrupar valores que difieren en un cierto número. Por ejemplo, vamos a decir que tengo esta tabla:¿Conseguir que todas las filas consecutivas difieran en cierto valor?

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 

Y vamos a decir que tengo para agrupar todos los valores que difieren en 1 que tiene el mismo valor para A. Entonces estoy tratando de obtener una salida como esta:

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) y (8,3) (8,4) (8,5) (8,6) se han puesto en el mismo grupo, ya que difieren en un valor de 1. voy a mostrar mi primer intento:

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 

voy a hacer esto en una mesa grande (alrededor de 30 millones de filas) así que esperaba una forma mejor de hacerlo para valores arbitrarios (por ejemplo, no solo difiriendo en 1, sino que podría ser 2 o 3 que incorporaré más adelante en un procedimiento). ¿Alguna sugerencia sobre si mi enfoque está libre de errores y si se puede mejorar?

Respuesta

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 ¡Gracias por su tiempo! Esto funciona como yo quería. Verificare el rendimiento en este y regrese. Mi enfoque fue casi similar, pero me preocupaban las combinaciones múltiples. – Legend

3

Para el caso en que se diferencian por uno puede utilizar

;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 

Y más en general

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, que funciona a la perfección, respuesta muy bien pensado. – Lamak

+0

+1 ¡Funciona perfecto! Una aclaración rápida: ¿hay alguna forma natural de extender esto al caso en el que estoy viendo menos que o igual que en lugar de la diferencia absoluta? Es decir, para el caso de '@ Interval = 2', también obtiene valores que difieren en uno en el mismo grupo. Entonces, en este caso, agrupa '(8,3) (8,4) (8,5) (8,6)' en un grupo. – Legend

+0

@Legend - ¡Tendrán que pensar en eso! Creo que la respuesta de Mikael se basó en el menor o igual al asssumption. –

Cuestiones relacionadas