次の列を持つ単純なテーブルを想像してください:item_id
、date
そして値:
CREATE TABLE foo (item_id int, date date);
INSERT INTO foo(item_id, date)
VALUES
( 1, '2017-02-10' ),
( 2, '2017-02-10' ),
( 1, '2017-02-11' ),
( 1, '2017-02-12' ),
( 1, '2017-02-13' ),
( 2, '2017-02-13' ),
( 1, '2017-02-14' );
選択方法item_id
sテーブルに7つの連続した日レコードがありますか?
開始日と終了日は不明です。それは、任意の開始日から最大7日間連続して利用できるはずです。
MySQL 8はウィンドウ関数を提供します...
_SELECT item_id
FROM (
SELECT
item_id,
date,
count(coalesce(diff, 1)=1 OR null) OVER (PARTITION BY item_id ORDER BY date) seq
FROM (
SELECT
item_id,
date,
date - lag(date) OVER (PARTITION BY item_id ORDER BY date) AS diff
FROM foo
) AS t
) AS t2
GROUP BY item_id
HAVING max(seq) > 7;
_
これは私たちがインナーでやっていることです。
_SELECT
item_id,
date,
date - lag(date) OVER (PARTITION BY item_id ORDER BY date) AS diff
FROM foo
item_id | date | diff
---------+------------+------
1 | 2017-02-10 |
1 | 2017-02-11 | 1
1 | 2017-02-12 | 1
1 | 2017-02-13 | 1
1 | 2017-02-14 | 1
2 | 2017-02-10 |
2 | 2017-02-13 | 3
(7 rows)
_
ここで違いを返します。ここで必要なのは、日付の差が1であるものを分離することです。ここで差の結果がnullであると仮定するのは、減算する前の日付がないためであるので、1に設定します。 1がない場合、値をnull
に設定して、count()
がそれをスキップするようにします。
_SELECT
item_id,
date,
count(coalesce(diff, 1)=1 OR null) OVER (PARTITION BY item_id ORDER BY date) seq
FROM (
SELECT
item_id,
date,
date - lag(date) OVER (PARTITION BY item_id ORDER BY date) AS diff
FROM foo
) AS t;
item_id | date | seq
---------+------------+-----
1 | 2017-02-10 | 1
1 | 2017-02-11 | 2
1 | 2017-02-12 | 3
1 | 2017-02-13 | 4
1 | 2017-02-14 | 5
2 | 2017-02-10 | 1
2 | 2017-02-13 | 1
(7 rows)
_
この時点からは、_GROUP BY
_とHAVING
になります。
これは、MySQL 8がまだリリースされていないため、PostgreSQLでテストされました。 PostgreSQL を使用していない場合は、無料でダウンロードして確認してください。 MySQLに似ていますが、あらゆる点で優れています。
select
item_id
from foo
where
(
select count(distinct date) from foo d2
where d2.date>=foo.date and d2.date<=date_add(foo.date, interval 6 day)
and foo.item_id=d2.item_id -- if IDs need to be the same
) =7
order by item_id
したがって、各行の各日付を取り、6日を追加して7日間にします。その期間に7つの異なる日付がある場合、それらは連続している必要があります。
素朴なアプローチは、7つの自己結合を行うことです。
select f1.item_id, f1.dt, f7.dt
from foo f1
join foo f2
on date_add(f1.dt, interval 1 day) = f2.dt
and f1.item_id = f2.item_id
join foo f3
on date_add(f2.dt, interval 1 day) = f3.dt
and f2.item_id = f3.item_id
join foo f4
on date_add(f3.dt, interval 1 day) = f4.dt
and f3.item_id = f4.item_id
join foo f5
on date_add(f4.dt, interval 1 day) = f5.dt
and f4.item_id = f5.item_id
join foo f6
on date_add(f5.dt, interval 1 day) = f6.dt
and f5.item_id = f6.item_id
join foo f7
on date_add(f6.dt, interval 1 day) = f7.dt
and f6.item_id = f7.item_id;
少し単純な方法は、連続する間隔を見つけて、どの間隔が7日以上かを確認することです。
select item_id from (
select lower.item_id, lower.min_dt, min(upper.max_dt) as max_dt
from (
select item_id, dt as min_dt
from foo f1
where not exists (
select 1 from foo f2
where f1.item_id = f2.item_id
and f1.dt = date_add(f2.dt, interval 1 day)
)
) as lower
join (
select item_id, dt as max_dt
from foo f3
where not exists (
select 1 from foo f4
where f3.item_id = f4.item_id
and f3.dt = date_sub(f4.dt, interval 1 day)
)
) as upper
on lower.item_id = upper.item_id
and max_dt >= min_dt
group by lower.item_id, lower.min_dt
) as x
where datediff(max_dt, min_dt) >= 7;
誰かがコメントで述べたように、この種の問題はウィンドウ関数のそよ風です。