0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MySQLで期間の重複をグループ化するクエリの解説

Posted at

今日は、MySQLを使って期間の重複をグループ化する方法について解説します。具体的には、利用開始日 (PERIOD_STT) と利用終了日 (PERIOD_END) が重複する期間を統合し、一意のグループとして扱う方法をご紹介します。

テーブルの構成

まず、以下のようなテーブル tbl を例にして考えます:

NUMBER	PERIOD_STT	PERIOD_END
1	2024-01-01	2024-03-01
2	2024-02-01	2024-03-01
3	2024-03-01	2024-06-01

このテーブルには、利用開始日と利用終了日が記録されています。これらの期間が重複している場合には、それらを統合して1つのグループとしてまとめたいと考えています。

クエリの構成

以下に示すクエリでは、期間が重複するデータをグループ化し、最終的に統合した期間を取得する方法を説明します。

WITH t1 AS (
    SELECT
        tbl.NUMBER AS number,
        tbl.PERIOD_STT AS period_stt,
        tbl.PERIOD_END AS period_end,
        LAG(tbl.PERIOD_STT) OVER (PARTITION BY tbl.NUMBER ORDER BY tbl.PERIOD_STT, tbl.PERIOD_END) AS lag_STT,
        LAG(tbl.PERIOD_END) OVER (PARTITION BY tbl.NUMBER ORDER BY tbl.PERIOD_STT, tbl.PERIOD_END) AS lag_END
    FROM
        tbl
),
t2 AS (
    SELECT
        number,
        period_stt,
        period_end,
        MIN(lag_STT) OVER (PARTITION BY number ORDER BY period_stt, period_end) AS min_lag_STT,
        MAX(lag_END) OVER (PARTITION BY number ORDER BY period_stt, period_end) AS max_lag_END
    FROM
        t1
),
t3 AS (
    SELECT
        number,
        period_stt,
        period_end,
        SUM(
            CASE
                WHEN period_stt <= max_lag_END AND period_end >= min_lag_STT THEN 0
                ELSE 1
            END
        ) OVER (PARTITION BY number ORDER BY period_stt, period_end) AS grp_key
    FROM
        t2
),
t4 AS (
    SELECT
        MAX(number) AS number,
        MIN(period_stt) AS period_stt,
        MAX(period_end) AS period_end
    FROM
        t3
    GROUP BY
        grp_key
),
t5 AS (
    SELECT
        t4.number,
        t4.period_stt,
        t4.period_end
    FROM
        t4
    INNER JOIN
        t1
    ON  t4.number = t1.number
)
SELECT
    t1.*
FROM
    t1
    LEFT OUTER JOIN
        t5
    ON  t5.number = t1.number
WHERE
    t5.number IS NULL;

クエリの解説

t1サブクエリ

各行に対して、前の行の利用開始日 (lag_STT) と利用終了日 (lag_END) を取得します。これにより、重複する期間を検出する準備をします。

t2サブクエリ

各行に対して、グループ化の基準となる最小の利用開始日 (min_lag_STT) と最大の利用終了日 (max_lag_END) を計算します。

t3サブクエリ

period_stt が max_lag_END 以下で、かつ period_end が min_lag_STT 以上の場合には、同じグループに属するものとしてカウントします。重複がある場合には grp_key が同じになります。

t4サブクエリ

grp_key でグループ化し、各グループの最小の利用開始日と最大の利用終了日を取得します。

t5サブクエリ

t4 の結果と t1 を内部結合し、最終的なグループ化された結果を取得します。

最終クエリ

t1 と t5 を左外部結合し、t5 に含まれていない行を抽出します。これにより、グループ化されなかった(つまり、重複がなかった)元の期間データが取得されます。

結果

例えば、以下のようにデータがあった場合:

NUMBER	PERIOD_STT	PERIOD_END
1	2024-01-01	2024-03-01
2	2024-02-01	2024-03-01
3	2024-03-01	2024-06-01

このクエリの結果、以下のように重複した期間が統合された結果が得られます:

NUMBER	PERIOD_STT	PERIOD_END
1	2024-01-01	2024-03-01
3	2024-03-01	2024-06-01

このようにして、利用開始日と利用終了日が重複しているデータを一つのグループとして統合することができます。クエリの各ステップを理解し、適切な集約処理を行うことで、重複を適切に処理できます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?