メモ:同じ値は同じグループにするけど、null は 全部別扱いしたいクエリ (dense_rank)
タイトルの通りですが、
select * from (values (10) , (20) , (null) , (null) , (10) , (30) , (40) ) as [t] ([Id]) order by [Id]
なデータがあった時に、Id が同じ物は同じグループにしたい場合に、
DENSE_RANK (Transact-SQL) - SQL Server | Microsoft Learn
ってのを使うんですが、null が複数あると null は同じグループになります。
select * , dense_rank() over (order by [Id]) as [GroupId] from (values (10) , (20) , (null) , (null) , (10) , (30) , (40) ) as [t] ([Id]) order by [Id]
要件的に同じで良い場合はこれでいいんですが、null は 別扱いしたい!!って場合のメモです。
元データに一意になる値があれば、null の時に別物扱い出来ます。
今回はシンプルな例なので、一意の列が無いため 、
ROW_NUMBER (Transact-SQL) - SQL Server | Microsoft Learn
で突っ込みます。*1
select * , row_number() over (order by [Id]) as [rownum] from (values (10) , (20) , (null) , (null) , (10) , (30) , (40) ) as [t] ([Id]) order by [Id]
で、dense_rank の order by をちょっと工夫するだけです。
select [Id] , dense_rank() over ( order by case when [Id] is null then concat('NULL', [rownum]) else concat('', [Id]) end) as [GroupId] from ( select * , row_number() over (order by [Id]) as [rownum] from (values (10) , (20) , (null) , (null) , (10) , (30) , (40) ) as [t] ([Id]) ) [tt] order by [Id]
null の時は、一意の列、そうじゃない時は、Id 列 使う。
ただ、一意の列の値と Id 列の値が被る場合があるので、被らないような一工夫する感じです。
ここでは、文字列化して、null の場合は接頭語 'NULL' つけてます。
*1:既にある場合は不要