Bölüm üzerinde farklı SQL sayma


10

İki sütun içeren bir tablo var, Col_B üzerinde (tarafından koşullandırılmış) Col_B üzerinde farklı değerleri saymak istiyorum.

Benim masam

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

Beklenen Sonuç

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

Aşağıdaki kodu denedim

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

count (farklı col_B) çalışmıyor. Farklı değerleri saymak için sayma işlevini nasıl yeniden yazabilirim?

Yanıtlar:


18

Ben böyle yapardı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;

GROUP BYFıkra söz konusu sağlanan verilerin gereksiz verilir, ancak size daha iyi yürütme planını verebilir. Q & A CROSS UYGULAMASI dış birleşim üreten takip bölümüne bakın .

OVER yan tümcesi geliştirme isteği için oy vermeyi düşünün - Bu özelliğin SQL Server'a eklenmesini istiyorsanız, geri bildirim sitesindeki toplama işlevleri için DISTINCT yan tümcesi .


6

Bunu kullanarak öykünebilir dense_rankve ardından her bölüm için maksimum sıralamayı seçebilirsiniz:

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    

İle col_baynı sonuçları almak için null değerlerini hariç tutmanız gerekir COUNT(DISTINCT).


6

Bu, bir bakıma, Lennart'ın çözümünün bir uzantısıdır , ancak o kadar çirkin ki, bunu bir düzenleme olarak önermeye cesaret edemem. Buradaki amaç, sonuçları türetilmiş bir tablo olmadan elde etmektir. Buna asla ihtiyaç olmayabilir ve sorgunun çirkinliği ile birleşince tüm çaba boşa giden bir çaba gibi görünebilir. Yine de bunu bir egzersiz olarak yapmak istedim ve şimdi sonucumu paylaşmak istiyorum:

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
;

Hesaplamanın temel kısmı şudur (ve her şeyden önce fikrin benim olmadığını belirtmek isterim, bu numarayı başka bir yerde öğrendim):

  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

Bu ifadenin, değerlerin Col_Bhiçbir zaman sıfır olmadığı garanti edilirse, herhangi bir değişiklik yapılmadan kullanılabilir . Sütunda null değerler varsa, bunu hesaba katmanız gerekir ve CASEifadenin tam olarak bunun için var olduğunu gösterir. O sayısı ile bölüm başına satır sayısını karşılaştırır Col_Bdeğerlerine bölüm başına. Sayılar farklıysa, bazı satırların boş olduğu anlamına gelir Col_Bve bu nedenle ilk hesaplamanın ( DENSE_RANK() ... + DENSE_RANK() - 1) 1 azaltılması gerekir.

Çünkü Not - 1çekirdek formül parçasıdır, böyle bırakmak seçtik. Bununla birlikte, CASEtüm çözümün daha az çirkin görünmesi için boşuna denemede aslında ifadeye dahil edilebilir :

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
;

dbfiddle logosuDb <> fiddle.uk adresindeki bu canlı demo , çözümün her iki varyasyonunu test etmek için kullanılabilir.


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

Benim gibi ilişkili alt sorgulara (Erik Darling'in cevabı) ve CTE'lere (kevinnwhat'ın cevabı) hafif alerjiniz varsa alternatiftir.

Null'lar karışıma atıldığında, bunların hiçbirinin nasıl olmasını istediğinizi sağlayamayabileceğini unutmayın. (ancak bunları tadına göre değiştirmek oldukça basittir)

Basit vaka:

--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]
;

Yukarıdaki ile aynı, ancak boş işleme için neyin değiştirileceği hakkında yorumlarla:

--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)
*/
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.