web-dev-qa-db-ja.com

テーブル内の値の連続発生数のカウント

下の表があります

create table #t (Id int, Name char)

insert into #t values
(1, 'A'),
(2, 'A'),
(3, 'B'),
(4, 'B'),
(5, 'B'),
(6, 'B'),
(7, 'C'),
(8, 'B'),
(9, 'B')

名前列の連続値を数えたい

+------+------------+
| Name | Repetition |
+------+------------+
| A    |          2 |
| B    |          4 |
| C    |          1 |
| B    |          2 |
+------+------------+

私が試した最高のものは:

select Name
, COUNT(*) over (partition by Name order by Id) AS Repetition
from #t
order by Id

しかし、それは私に期待した結果を与えません

23
FLICKER

1つのアプローチは、行番号の違いです。

select name, count(*) 
from (select t.*,
             (row_number() over (order by id) -
              row_number() over (partition by name order by id)
             ) as grp
      from t
     ) t
group by grp, name;

ロジックは、サブクエリを実行して各行番号の値を個別に調べてから、その違いを調べると理解しやすくなります。

27
Gordon Linoff

私は再帰CTEを使用して、row_numberの使用を最小限に抑え、count(*)も回避しています。

パフォーマンスは向上すると思いますが、実際には、影響を受ける行の数を最小限に抑えるためにフィルターを他にどのように配置するかによって異なります。

IDの値が目立たない場合は、連続するIDを生成するために1つの追加のCTEが使用されます。

 ;With CTE2 as
(
select ROW_NUMBER()over(order by id) id, name,1 Repetition ,1 Marker  from @t
)
, CTE as
(
select top 1 cast(id as int) id, name,1 Repetition ,1 Marker  from CTE2 order by id

union all

select a.id, a.name
, case when a.name=c.name then Repetition +1 else 1 end  
, case when a.name=c.name then c.Marker else  Marker+1 end
from @t a
inner join CTE c on a.id=c.id+1

)
,CTE1 as
(select *,ROW_NUMBER()over(partition by marker order by id desc)rn from cte c
)
select Name,Repetition from cte1 where rn=1
2
KumarHarsh

LAGや積算合計などのウィンドウ関数を使用できます。

WITH cte AS (
 SELECT Id, Name, grp = SUM(CASE WHEN Name = prev THEN 0 ELSE 1 END) OVER(ORDER BY id)
 FROM (SELECT *, prev = LAG(Name) OVER(ORDER BY id) FROM t) s
)
SELECT name, cnt = COUNT(*)
FROM cte
GROUP BY grp,name
ORDER BY grp;

db <> fiddle demo

最初のcteはグループ番号を返します:

+-----+-------+-----+
| Id  | Name  | grp |
+-----+-------+-----+
|  1  | A     |   1 |
|  2  | A     |   1 |
|  3  | B     |   2 |
|  4  | B     |   2 |
|  5  | B     |   2 |
|  6  | B     |   2 |
|  7  | C     |   3 |
|  8  | B     |   4 |
|  9  | B     |   4 |
+-----+-------+-----+

そしてメインクエリは、以前に計算されたgrp列に基づいてそれをグループ化します:

+-------+-----+
| name  | cnt |
+-------+-----+
| A     |   2 |
| B     |   4 |
| C     |   1 |
| B     |   2 |
+-------+-----+
0
Lukasz Szozda