SQL đếm khác biệt trên phân vùng


10

Tôi có một bảng có hai cột, tôi muốn đếm các giá trị riêng biệt trên Col_B trên (được quy định bởi) Col_A.

MyTable

Col_A | Col_B 
A     | 1
A     | 1
A     | 2
A     | 2
A     | 2
A     | 3
b     | 4
b     | 4
b     | 5

Kết quả mong đợi

Col_A   | Col_B | Result
A       | 1     | 3
A       | 1     | 3
A       | 2     | 3
A       | 2     | 3
A       | 2     | 3
A       | 3     | 3
b       | 4     | 2
b       | 4     | 2
b       | 5     | 2

Tôi đã thử đoạn mã sau

select *, 
count (distinct col_B) over (partition by col_A) as 'Result'
from MyTable

đếm (col_B riêng biệt) không hoạt động. Làm thế nào tôi có thể viết lại hàm đếm để đếm các giá trị khác biệt?

Câu trả lời:


18

Đây là cách tôi sẽ làm:

SELECT      *
FROM        #MyTable AS mt
CROSS APPLY (   SELECT COUNT(DISTINCT mt2.Col_B) AS dc
                FROM   #MyTable AS mt2
                WHERE  mt2.Col_A = mt.Col_A
                -- GROUP BY mt2.Col_A 
            ) AS ca;

Các GROUP BYkhoản được thừa cho các dữ liệu được cung cấp trong câu hỏi, nhưng có thể cung cấp cho bạn một kế hoạch thực hiện tốt hơn. Xem phần Q & A CROSS ỨNG DỤNG tiếp theo tạo ra sự tham gia bên ngoài .

Xem xét bỏ phiếu cho yêu cầu nâng cao mệnh đề OVER - mệnh đề DISTINCT cho các hàm tổng hợp trên trang web phản hồi nếu bạn muốn tính năng đó được thêm vào SQL Server.


6

Bạn có thể mô phỏng nó bằng cách sử dụng dense_rank, sau đó chọn thứ hạng tối đa cho mỗi phân vùng:

select col_a, col_b, max(rnk) over (partition by col_a)
from (
    select col_a, col_b
        , dense_rank() over (partition by col_A order by col_b) as rnk 
    from #mytable
) as t    

Bạn sẽ cần loại trừ mọi giá trị null col_bđể có kết quả tương tự COUNT(DISTINCT).


6

Theo một cách nào đó, đây là một phần mở rộng cho giải pháp của Lennart , nhưng nó xấu đến mức tôi không dám đề xuất nó như là một chỉnh sửa. Mục tiêu ở đây là để có được kết quả mà không cần bảng dẫn xuất. Có thể không bao giờ cần điều đó, và kết hợp với sự xấu xí của truy vấn, toàn bộ nỗ lực có vẻ như là một nỗ lực lãng phí. Tôi vẫn muốn làm điều này như một bài tập, mặc dù, và bây giờ muốn chia sẻ kết quả của tôi:

SELECT
  Col_A,
  Col_B,
  DistinctCount = DENSE_RANK() OVER (PARTITION BY Col_A ORDER BY Col_B ASC )
                + DENSE_RANK() OVER (PARTITION BY Col_A ORDER BY Col_B DESC)
                - 1
                - CASE COUNT(Col_B) OVER (PARTITION BY Col_A)
                  WHEN COUNT(  *  ) OVER (PARTITION BY Col_A)
                  THEN 0
                  ELSE 1
                  END
FROM
  dbo.MyTable
;

Phần cốt lõi của phép tính là điều này (và trước hết tôi muốn lưu ý rằng ý tưởng không phải là của tôi, tôi đã tìm hiểu về thủ thuật này ở nơi khác):

  DENSE_RANK() OVER (PARTITION BY Col_A ORDER BY Col_B ASC )
+ DENSE_RANK() OVER (PARTITION BY Col_A ORDER BY Col_B DESC)
- 1

Biểu thức này có thể được sử dụng mà không có bất kỳ thay đổi nào nếu các giá trị trong Col_Bđược đảm bảo không bao giờ có giá trị null. Tuy nhiên, nếu cột có thể có giá trị null, bạn cần tính đến điều đó và đó chính xác là CASEbiểu thức có ở đó. Nó so sánh số lượng hàng trên mỗi phân vùng với số lượng Col_Bgiá trị trên mỗi phân vùng. Nếu các số khác nhau, điều đó có nghĩa là một số hàng có giá trị null Col_Bvà do đó, phép tính ban đầu ( DENSE_RANK() ... + DENSE_RANK() - 1) cần phải giảm đi 1.

Lưu ý rằng vì - 1là một phần của công thức cốt lõi, tôi đã chọn để nó như thế. Tuy nhiên, nó thực sự có thể được tích hợp vào CASEbiểu thức, trong nỗ lực vô ích để làm cho toàn bộ giải pháp trông bớt xấu xí:

SELECT
  Col_A,
  Col_B,
  DistinctCount = DENSE_RANK() OVER (PARTITION BY Col_A ORDER BY Col_B ASC )
                + DENSE_RANK() OVER (PARTITION BY Col_A ORDER BY Col_B DESC)
                - CASE COUNT(Col_B) OVER (PARTITION BY Col_A)
                  WHEN COUNT(  *  ) OVER (PARTITION BY Col_A)
                  THEN 1
                  ELSE 2
                  END
FROM
  dbo.MyTable
;

Bản demo trực tiếp này tại logo dbfiddledb <> fiddle.uk có thể được sử dụng để kiểm tra cả hai biến thể của giải pháp.


2
create table #MyTable (
Col_A varchar(5),
Col_B int
)

insert into #MyTable values ('A',1)
insert into #MyTable values ('A',1)
insert into #MyTable values ('A',2)
insert into #MyTable values ('A',2)
insert into #MyTable values ('A',2)
insert into #MyTable values ('A',3)

insert into #MyTable values ('B',4)
insert into #MyTable values ('B',4)
insert into #MyTable values ('B',5)


;with t1 as (

select t.Col_A,
       count(*) cnt
 from (
    select Col_A,
           Col_B,
           count(*) as ct
      from #MyTable
     group by Col_A,
              Col_B
  ) t
  group by t.Col_A
 )

select a.*,
       t1.cnt
  from #myTable a
  join t1
    on a.Col_A = t1.Col_a

1

Thay thế nếu bạn bị dị ứng nhẹ với các truy vấn con tương quan (câu trả lời của Erik Darling) và CTE (câu trả lời của kevinnwhat) như tôi.

Xin lưu ý rằng khi null được đưa vào hỗn hợp, không ai trong số này có thể hoạt động như bạn muốn. (nhưng nó khá đơn giản để sửa đổi chúng thành hương vị)

Trường hợp đơn giản:

--ignore the existence of nulls
SELECT [mt].*, [Distinct_B].[Distinct_B]
FROM #MyTable AS [mt]

INNER JOIN(
    SELECT [Col_A], COUNT(DISTINCT [Col_B]) AS [Distinct_B]
    FROM #MyTable
    GROUP BY [Col_A]
) AS [Distinct_B] ON
    [mt].[Col_A] = [Distinct_B].[Col_A]
;

Tương tự như trên, nhưng với ý kiến ​​về những gì cần thay đổi để xử lý null:

--customizable null handling
SELECT [mt].*, [Distinct_B].[Distinct_B]
FROM #MyTable AS [mt]

INNER JOIN(
    SELECT 

    [Col_A],

    (
        COUNT(DISTINCT [Col_B])
        /*
        --uncomment if you also want to count Col_B NULL
        --as a distinct value
        +
        MAX(
            CASE
                WHEN [Col_B] IS NULL
                THEN 1
                ELSE 0
            END
        )
        */
    )
    AS [Distinct_B]

    FROM #MyTable
    GROUP BY [Col_A]
) AS [Distinct_B] ON
    [mt].[Col_A] = [Distinct_B].[Col_A]
/*
--uncomment if you also want to include Col_A when it's NULL
OR
([mt].[Col_A] IS NULL AND [Distinct_B].[Col_A] IS NULL)
*/
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.