2011-10-21 14 views
6

Ardışık satırların karşılaştırmasını içerdiğinden, bunu yaparken kafamı çekmeye çalışıyorum. Belirli bir sayıya göre farklılık gösteren değerleri gruplandırmaya çalışıyorum. Örneğin, bize bu tabloyu olduğunu varsayalım:Tüm ardışık satırları belirli bir değere göre farklılaştırmak ister misiniz?

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 

Ve bize Gruba Sonra bu şöyle bir çıktı elde çalışıyorum A. için aynı değere sahip 1 farklı tüm değerlere sahip varsayalım: onlar farklı olduğundan

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) ve (8,3) (8,4) (8,5) (8,6) ben ilk benim girişimi gösterecek bir değere 1. tarafından aynı grup konulmuştur:

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 
Ben bu yapıyor olacak

büyük bir masa (yaklaşık 30 milyon satır) bu yüzden keyfi değerler için bunu yapmanın daha iyi bir yolu olduğunu umuyorum (örneğin, yalnızca 1 ile farklılık göstermeyen, ancak daha sonra bir prosedürün içine dahil edeceğim 2 veya 3 olabilir). Benim yaklaşımımın hatasız olup olmadığı ve geliştirilebileceği konusunda herhangi bir öneriniz var mı?

onlar birer farklılık durum için

cevap

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 Zaman ayırdığınız için teşekkür ederiz! Bu istediğim gibi çalışıyor. Bu konuda performansı kontrol edip geri döneceğim. Yaklaşımım neredeyse birbirine benziyordu, ancak çoklu katılımlar konusunda endişeliydim. – Legend

3

Eğer

;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 

kullanabilirsiniz Ve daha genel

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, çok iyi düşünülmüş cevap, kusursuz çalışır. – Lamak

+0

+1 Mükemmel çalışıyor! Hızlı bir açıklama: Bunu, mutlak fark yerine daha az veya eşit olarak baktığım duruma genişletmenin doğal bir yolu var mı? Yani, @ @ Interval = 2' durumunda, aynı gruba göre farklılık gösteren değerler alır. Dolayısıyla bu durumda, (8,3) (8,4) (8,5) (8,6) 'yi bir gruba ayırır. – Legend

+0

@Legend - Bunu düşünmek zorunda kalacak! Bence Mikael'in cevabı, az ya da eşit tutmaya dayanıyordu. –

İlgili konular