SQLはパーティション全体で個別にカウントされます


10

2つの列を持つテーブルがあります。Col_Aで(条件付けされた)Col_Bの個別の値をカウントします。

MyTable

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

期待される結果

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

私は次のコードを試しました

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

count(distinct col_B)が機能していません。個別の値をカウントするようにカウント関数を書き換えるにはどうすればよいですか?

回答:


18

これは私がそれをする方法です:

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 BY句は疑問に提供されたデータを冗長与えられるが、あなたがより良い実行計画を与える可能性があります。フォローアップのQ&A CROSS APPLYが外部結合を生成するを参照してください。

SQL Serverにその機能を追加したい場合は、フィードバックサイトの集計関数のOVER句拡張リクエスト-DISTINCT句に投票することを検討してください。


6

を使用してそれをエミュレートしdense_rank、各パーティションの最大ランクを選択できます。

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    

col_bと同じ結果を得るには、からnullを除外する必要がありCOUNT(DISTINCT)ます。


6

これは、ある意味、レナートのソリューションの拡張ですが、あまりにも醜いので、編集として提案するつもりはありません。ここでの目標は、派生テーブルなしで結果を取得することです。それが必要になることは決してなく、クエリの醜さと相まって、全体の努力は無駄な努力のように思えるかもしれません。私はまだこれを演習として行いたいと思っていましたが、今私の結果を共有したいと思います:

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
;

計算の核となる部分は次のとおりです(そして最初に、このアイデアは私のものではないことを指摘しておきます。このトリックについては他の場所で学びました)。

  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

の値にCol_Bnullが含まれないことが保証されている場合は、この式をそのまま使用できます。ただし、列にnullが含まれる可能性がある場合は、それを考慮する必要があり、それがまさにCASE式の目的です。パーティションごとの行数とパーティションごとのCol_Bの数を比較します。数値が異なる場合は、一部の行にnullが含まれているCol_Bため、最初の計算(DENSE_RANK() ... + DENSE_RANK() - 1)を1減らす必要があります。

- 1はコア式の一部であるため、そのままにしておくことにしました。ただし、実際にCASE式に組み込んで、ソリューション全体の見栄えを悪くするという無駄な試みに利用できます。

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ロゴdb <> fiddle.ukにあるこのライブデモを使用して、ソリューションの両方のバリエーションをテストできます。


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

私と同じように相関サブクエリ(Erik Darlingの回答)とCTE(kevinnwhatの回答)に軽度のアレルギーがある場合の代替案。

nullがミックスに投入された場合、これらのどれもが希望どおりに機能しない可能性があることに注意してください。 (しかし、好みに合わせて変更するのはかなり簡単です)

単純なケース:

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

上記と同じですが、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)
*/
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.