2015-06-06 11 views
6

Tôi đang vật lộn với một truy vấn SQL phức tạp mà tôi đang cố gắng viết. Hãy xem bảng sau:T-SQL - Lấy danh sách tất cả Vì có cùng một tập hợp của Bs

+---+---+ 
| A | B | 
+---+---+ 
| 1 | 2 | 
| 1 | 3 | 
| 2 | 2 | 
| 2 | 3 | 
| 2 | 4 | 
| 3 | 2 | 
| 3 | 3 | 
| 4 | 2 | 
| 4 | 3 | 
| 4 | 4 | 
+---+---+ 

Bây giờ, về cơ bản, tôi muốn danh sách tất cả có cùng một bộ Bs và đặt mỗi ID gia tăng.

Do đó, sản lượng đề ra cho ở trên sẽ là:

+---+----+ 
| A | ID | 
+---+----+ 
| 1 | 1 | 
| 3 | 1 | 
| 2 | 2 | 
| 4 | 2 | 
+---+----+ 

Cảm ơn.

Chỉnh sửa: Nếu trợ giúp, tôi có danh sách tất cả các giá trị khác biệt của B có thể có trong bảng khác.

Chỉnh sửa: Cảm ơn bạn rất nhiều vì tất cả các câu trả lời sáng tạo. Đã có thể học rất nhiều.

+0

Nếu bạn có thêm 3 hàng: '4,2 | 4,3 | 4,4'? – edc65

+0

Tôi đã suy nghĩ về việc lấy một danh sách riêng biệt của Như, sau đó lưu trữ danh sách CSV của Bs cho mỗi A trong một bảng tạm thời. Cuối cùng, tôi sẽ tìm thấy các mục trong bảng có cùng chuỗi CSV. –

+0

Có mẹo lừa để tạo chuỗi CSV bằng hàm xml, không cần bảng tạm thời: http://sqlandme.com/2011/04/27/tsql-concatenate-rows-using-for-xml-path/ – edc65

Trả lời

5

Đây là thủ thuật toán học để giải quyết khó khăn chọn của bạn:

with pow as(select *, b * power(10, row_number() 
       over(partition by a order by b)) as rn from t) 
select a, dense_rank() over(order by sum(rn)) as rn 
from pow 
group by a 
order by rn, a 

Fiddle http://sqlfiddle.com/#!3/6b98d/11

Điều này tất nhiên sẽ làm việc chỉ để đếm riêng biệt hạn chế như bạn sẽ nhận được tràn. Đây là giải pháp tổng quát hơn với chuỗi:

select a, 
dense_rank() over(order by (select '.' + cast(b as varchar(max)) 
          from t t2 where t1.a = t2.a 
          order by b 
          for xml path(''))) rn 
from t t1 
group by a 
order by rn, a 

Fiddle http://sqlfiddle.com/#!3/6b98d/29

+3

Mặc dù tôi upvoted, bạn nên thay đổi hai điều. Trước tiên, bạn thực sự cần một dấu tách cho các giá trị 'b' trong chuỗi giải pháp (có bao nhiêu phần tử nằm trong" 123 "). Thứ hai, bạn không bao giờ nên sử dụng 'varchar()' mà không có tham số chiều dài trong SQL Server. Độ dài mặc định thay đổi theo ngữ cảnh; đang rõ ràng ngăn chặn các lỗi khác. –

+0

@GordonLinoff, bạn hoàn toàn đúng. 123 và 12 và 3 sẽ có cùng thứ hạng. Nắm bắt tốt! –

2

EDIT Tôi thay đổi mã, nhưng nó sẽ trở nên lớn hơn bây giờ, mất sự giúp đỡ từ Concatenate many rows into a single text string? cho concatinating chuỗi

Select [A], 
    Left(M.[C],Len(M.[C])-1) As [D] into #tempSomeTable 
From 
(
    Select distinct T2.[A], 
     (
      Select Cast(T1.[B] as VARCHAR) + ',' AS [text()] 
      From sometable T1 
      Where T1.[A] = T2.[A] 
      ORDER BY T1.[A] 
      For XML PATH ('') 
     ) [C] 
    From sometable T2 
)M 

    SELECT t.A, DENSE_RANK() OVER(ORDER BY t.[D]) [ID] FROM 
    #tempSomeTable t 
    inner join 
    (SELECT [D] FROM(
    SELECT [D], COUNT([A]) [D_A] from 
    #tempSomeTable t 
    GROUP BY [D])P where [C_A]>1)t1 on t1.[D]=t.[D] 
+0

Tổng? thử với '2,4,6' và' 1,3,8' – edc65

+0

Cảm ơn, thay đổi nó thành so sánh chuỗi. – debatanu

+0

Không hoạt động bây giờ – edc65

2

Dưới đây là một cách tiếp cận dài hơi, bằng cách tìm bộ với các yếu tố tương tự (sử dụng EXCEPT hai chiều để loại bỏ, và chỉ cần thực hiện một sản phẩm Descartes nửa đường chéo), sau đó ghép nối bộ bằng lên, dập mỗi cặp với một ROW_NUMBER(), trước khi unpivoting các cặp A's vào để kết quả cuối cùng của bạn, nơi các bộ tương đương được dự đoán là hàng có sa tôi id.

WITH joinedSets AS 
(
    SELECT t1.A as t1A, t2.A AS t2A 
    FROM MyTable t1 
    INNER JOIN MyTable t2 
    ON t1.B = t2.B 
     AND t1.A < t2.A 
), 
equalSets AS 
(
    SELECT js.t1A, js.t2A, ROW_NUMBER() OVER (ORDER BY js.t1A) AS Id 
    FROM joinedSets js 
    GROUP BY js.t1A, js.t2A 
    HAVING NOT EXISTS ((SELECT mt.B FROM MyTable mt WHERE mt.A = js.t1A) 
      EXCEPT (SELECT mt.B FROM MyTable mt WHERE mt.A = js.t2A)) 
     AND NOT EXISTS ((SELECT mt.B FROM MyTable mt WHERE mt.A = js.t2A) 
      EXCEPT (SELECT mt.B FROM MyTable mt WHERE mt.A = js.t1A)) 
) 
SELECT A, Id 
FROM equalSets 
UNPIVOT 
(
    A 
    FOR ACol in (t1A, t2A) 
) unp; 

SqlFiddle here

Khi đứng, giải pháp này sẽ chỉ làm việc với cặp bộ, không gấp ba vv Một chung giải pháp loại NTuple có lẽ là có thể (nhưng ngoài bộ não của tôi ngay bây giờ).

3

Something như thế này:

select a, dense_rank() over (order by g) as id_b 
from (
    select a, 
    (select b from MyTable s where s.a=a.a order by b FOR XML PATH('')) g 
    from MyTable a 
    group by a 
) a 
order by id_b,a 

Hoặc có thể sử dụng một CTE (Tôi tránh chúng khi có thể)

Sql Fiddle

Như một mặt lưu ý, đây là ra đặt truy vấn bên trong bằng cách sử dụng dữ liệu mẫu trong câu hỏi:

a g 
1 <b>2</b><b>3</b> 
2 <b>2</b><b>3</b><b>4</b> 
3 <b>2</b><b>3</b> 
4 <b>2</b><b>3</b><b>4</b> 
+0

Số hàng sẽ không cho bạn kết quả phù hợp nếu tôi không nhầm. –

+0

Bạn đã đúng, xin lỗi. Tôi nghĩ rằng nó có thể được thực hiện với row_number, nhưng dense_rank là phù hợp hơn. – edc65

+0

Xem nhận xét của tôi @ Giải pháp tương đương của Giorgi. –

2

Đây là giải pháp rất đơn giản, nhanh chóng nhưng gần đúng. Có thể là CHECKSUM_AGG trả về checksum tương tự cho bộ khác nhau của B.

DECLARE @T TABLE (A int, B int); 

INSERT INTO @T VALUES 
(1, 2),(1, 3),(2, 2),(2, 3),(2, 4),(3, 2),(3, 3),(4, 2),(4, 3),(4, 4); 

SELECT 
    A 
    ,CHECKSUM_AGG(B) AS CheckSumB 
    ,ROW_NUMBER() OVER (PARTITION BY CHECKSUM_AGG(B) ORDER BY A) AS GroupNumber 
FROM @T 
GROUP BY A 
ORDER BY A, GroupNumber; 

quả thiết

A CheckSumB GroupNumber 
----------------------------- 
1 1   1 
2 5   1 
3 1   2 
4 5   2 

Đối với nhóm giải pháp chính xác bởi A và nối tất cả B giá trị vào một chặng đường dài (nhị phân) chuỗi sử dụng hàm FOR XML, CLR hoặc T-SQL. Sau đó, bạn có thể phân vùng ROW_NUMBER bằng chuỗi nối đó để gán số cho các nhóm. Như được thể hiện trong các câu trả lời khác.

0

Đây là giải pháp chính xác, chứ không phải là gần đúng. Nó không sử dụng gì cao cấp hơn INNER JOIN và GROUP BY (và, tất nhiên, DENSE_RANK() để lấy ID bạn muốn).

Nó cũng chung, ở chỗ nó cho phép lặp lại các giá trị B trong một nhóm A.

SELECT A, 
     DENSE_RANK() OVER (ORDER BY MIN_EQUIVALENT_A) AS ID 

FROM  (
      SELECT MATCHES.A1 AS A, 
        MIN(MATCHES.A2) AS MIN_EQUIVALENT_A 

      FROM  (
        SELECT T1.A AS A1, 
          T2.A AS A2, 
          COUNT(*) AS NUM_B_VALS_MATCHED 

        FROM  (
           SELECT A, 
             B, 
             COUNT(*) AS B_VAL_FREQ 
           FROM  MyTable 
           GROUP BY A, 
             B 
          ) AS T1 

          INNER JOIN 

          (
           SELECT A, 
             B, 
             COUNT(*) AS B_VAL_FREQ 
           FROM  MyTable 
           GROUP BY A, 
             B 
          ) AS T2 

          ON T1.B = T2.B 
           AND T1.B_VAL_FREQ = T2.B_VAL_FREQ 

        GROUP BY T1.A, 
          T2.A 
        ) AS MATCHES 

        INNER JOIN 

        (
        SELECT A, 
          COUNT(DISTINCT B) AS NUM_B_VALS_TOTAL 
        FROM  MyTable 
        GROUP BY A 
        ) AS CHECK_TOTALS_A1 

        ON MATCHES.A1 = CHECK_TOTALS_A1.A 
         AND MATCHES.NUM_B_VALS_MATCHED 
          = CHECK_TOTALS_A1.NUM_B_VALS_TOTAL 

        INNER JOIN 

        (
        SELECT A, 
          COUNT(DISTINCT B) AS NUM_B_VALS_TOTAL 
        FROM  MyTable 
        GROUP BY A 
        ) AS CHECK_TOTALS_A2 

        ON MATCHES.A2 = CHECK_TOTALS_A2.A 
         AND MATCHES.NUM_B_VALS_MATCHED 
          = CHECK_TOTALS_A2.NUM_B_VALS_TOTAL 

      GROUP BY MATCHES.A1 
     ) AS EQUIVALENCE_TABLE 

ORDER BY 2,1 
; 
Các vấn đề liên quan