相関サブクエリを使って次回契約を取得する

相関サブクエリを使って次回契約を取得します。

Contract table

id account_id start_date end_date
1 1 20140101 20141231
2 1 20150101 20151231
3 1 20160101 20161231
4 1 20170101 20171231
5 2 20150101 20151231
6 2 20160101 20161231

上記のようなaccount_idと開始日、終了日の登録してあるテーブルがあるとします。
現在の契約を取得するのは簡単ですね。

1
2
# 現在契約を取得する
SELECT * FROM contracts WHERE start_date >= 現在日付 AND end_date <= 現在日付

現在契約を取得するのは簡単ですが、その次の契約を取得するとなるとそう単純にはいきません。

そこで、相関サブクエリを使います。
相関サブクエリを使うことで次回契約を取得できます。

1
2
3
4
5
6
7
8
9
10
11
12
# 次回契約を取得する
SELECT *
FROM contracts As cont
WHERE start_date = (SELECT MIN(start_date)
FROM contracts as c1
WHERE c1.start_date > ( SELECT end_date
FROM contracts as c2
WHERE c2.start_date <= '20150528'
AND c2.end_date >= '20150528'
AND c1.account_id = c2.account_id)
AND cont.account_id = c1.account_id
GROUP BY c1.account_id);

結果

id account_id start_date end_date
3 1 20160101 20161231
6 2 20160101 20161231

クエリの説明

クエリの説明をします。

クエリは内側からみていきます。
まずは、一番内側にある、 SELECT end_date ... AND c1.account_id = c2.account_idのクエリです。
このクエリでは現在の契約(ここでは2015/5/28とします)を取得します。

2つめのクエリで、次回以降の契約を取得します。
SELECT MIN(start_date) を使うことで、次回契約のなかで直近の契約を取得できます。
アカウントごとに直近の次回契約を取得したいので、GROUP BY account_id をしています。

c1.account_id = c2.account_id と cont.account_id = c1.account_id は行と行を比較するために必要です。

3つめのクエリ(SELECT * ... WHERE start_date =)で直近の次回契約を条件として、データを取得します。

手元で試したい方は以下のクエリでデータをつくれます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CREATE TABLE `contracts` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`account_id` int(11) DEFAULT NULL,
`start_date` int(11) DEFAULT NULL,
`end_date` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `contracts` (`id`, `account_id`, `start_date`, `end_date`)
VALUES
(1, 1, 20140101, 20141231),
(2, 1, 20150101, 20151231),
(3, 1, 20160101, 20161231),
(4, 1, 20170101, 20171231),
(5, 2, 20150101, 20151231),
(6, 2, 20160101, 20161231);