2011-10-21 34 views
6

Tôi đang cố gắng để có được đầu của tôi xung quanh làm điều này vì nó liên quan đến việc so sánh các hàng liên tiếp. Tôi đang cố gắng nhóm các giá trị khác nhau theo một số nhất định. Ví dụ: giả sử tôi có bảng này:Nhận tất cả các hàng liên tiếp khác nhau theo giá trị nhất định?

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 

Và chúng tôi nói rằng tôi phải nhóm tất cả các giá trị khác nhau 1 có cùng giá trị cho A. Sau đó, tôi cố gắng để có được kết quả như sau:

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)(8,3) (8,4) (8,5) (8,6) đã được đưa vào cùng một nhóm bởi vì chúng khác nhau bởi một giá trị 1. đầu tiên tôi sẽ cho nỗ lực của tôi:

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 

tôi sẽ làm điều này trên bảng lớn (khoảng 30 triệu hàng) vì vậy tôi hy vọng có một cách tốt hơn để làm điều này cho các giá trị tùy ý (ví dụ, không chỉ khác nhau 1, nhưng nó có thể là 2 hoặc 3 mà tôi sẽ kết hợp sau này vào một thủ tục). Bất kỳ đề xuất nào về việc liệu phương pháp tiếp cận của tôi có bị lỗi và liệu nó có thể được cải thiện không?

Trả lời

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 Cảm ơn bạn đã dành thời gian! Điều này hoạt động như tôi muốn. Tôi sẽ kiểm tra hiệu suất trên cái này và quay lại. Cách tiếp cận của tôi gần như tương tự nhưng tôi đã lo lắng về nhiều lần tham gia. – Legend

3

Đối với trường hợp chúng khác nhau bởi một bạn có thể sử dụng

;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 

Và tổng quát hơn

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, nó hoạt động hoàn hảo, rất tốt nghĩ câu trả lời. – Lamak

+0

+1 Hoạt động hoàn hảo! Một cách làm rõ nhanh: Có cách nào tự nhiên để mở rộng trường hợp này khi tôi đang nhìn ít hơn hoặc bằng thay vì khác biệt tuyệt đối không? Tức là, đối với trường hợp '@ Interval = 2', nó cũng nhận được các giá trị khác nhau theo một nhóm. Vì vậy, trong trường hợp này, nó nhóm '(8,3) (8,4) (8,5) (8,6)' thành một nhóm. – Legend

+0

@Đăng ký - Sẽ phải suy nghĩ về điều đó! Tôi nghĩ câu trả lời của Mikael dựa trên giả thuyết ít hơn hoặc bằng. –

Các vấn đề liên quan