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)が機能していません。個別の値をカウントするようにカウント関数を書き換えるにはどうすればよいですか?
これは私がそれをする方法です:
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句 に投票することを検討してください。
_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
_
COUNT(DISTINCT)
と同じ結果を得るには、_col_b
_からnullを除外する必要があります。
これはある意味で Lennartの解 の拡張ですが、あまりにも醜いので、編集として提案するつもりはありません。ここでの目標は、派生テーブルなしで結果を取得することです。それが必要になることは決してなく、クエリの醜さと相まって、全体の努力は無駄な努力のように思えるかもしれません。私はまだこれを演習として行いたいと思っていましたが、今私の結果を共有したいと思います:
_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_B
_の値にnullが含まれないことが保証されている場合は、この式をそのまま使用できます。ただし、列にnullを含めることができる場合は、そのことを考慮する必要があります。これが、CASE
式の目的です。パーティションごとの行数を、パーティションごとの_Col_B
_値の数と比較します。数値が異なる場合は、一部の行で_Col_B
_にnullが含まれているため、最初の計算(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
;
_
このライブデモ at db <> fiddle.ukを使用して、ソリューションの両方のバリエーションをテストできます。
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
私と同じように相関サブクエリ(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)
*/