选择不重叠的日期范围
Selecting ranges of dates without overlapping
我有以下关系模式:
dates(date: date, code: char)
代码可以是 ST、MN、MX、ED。
一个例子:
╔════════════╦══════╗
║ date ║ code ║
╠════════════╬══════╣
║ 2001-10-01 ║ ST ║
║ 2001-10-20 ║ ST ║
║ 2001-11-01 ║ MX ║
║ 2001-11-01 ║ MN ║
║ 2001-11-14 ║ MX ║
║ 2001-11-15 ║ ED ║
║ 2001-11-15 ║ MX ║
║ 2001-11-27 ║ MN ║
║ 2001-12-01 ║ ST ║
║ 2001-12-01 ║ ED ║
║ 2001-12-02 ║ MX ║
║ 2001-12-03 ║ MX ║
║ 2001-12-05 ║ ED ║
║ 2001-12-20 ║ ST ║
║ 2001-12-21 ║ MN ║
║ 2001-12-24 ║ MX ║
║ 2001-12-31 ║ ED ║
╚════════════╩══════╝
我需要:
- 查找从代码为 ST 的日期开始到代码为 ED 的日期结束的任何日期范围。
- 在这些范围内不能有任何以 ST 或 ED 作为代码的元组(范围不能重叠)。
- 无需程序,仅需一个 SELECT 语句(我可以使用 WITH)。
我用以下查询完成了第一部分:
SELECT DISTINCT ON (dt.date) dt.date AS start, dt1.date AS end
FROM dates AS dt, dates AS dt1
WHERE dt.type='ST' AND dt1.type='ED' AND dt.date<dt1.date;
不过我不知道如何消除重叠范围。
使用给定的示例数据我的查询输出:
╔════════════╦════════════╗
║ start ║ end ║
╠════════════╬════════════╣
║ 2001-10-01 ║ 2001-12-01 ║
║ 2001-10-20 ║ 2001-11-15 ║
║ 2001-12-01 ║ 2001-12-31 ║
║ 2001-12-20 ║ 2001-12-31 ║
╚════════════╩════════════╝
如您所见,第二个范围与第一个范围重叠,因此它没有按我的预期工作。
正确的输出应该是:
╔════════════╦════════════╗
║ start ║ end ║
╠════════════╬════════════╣
║ 2001-10-20 ║ 2001-11-15 ║
║ 2001-12-20 ║ 2001-12-31 ║
╚════════════╩════════════╝
如果我理解正确,那么你可以使用lead()
和where
来达到这个目的:
select date as startdate, next_date as enddate
from (select d.*,
lead(code) over (order by date) as next_code,
lead(date) over (order by date) as next_date
from dates d
where code in ('ST', 'ED')
) d
where code = 'ST' and
next_code = 'ED';
我有以下关系模式:
dates(date: date, code: char)
代码可以是 ST、MN、MX、ED。 一个例子:
╔════════════╦══════╗
║ date ║ code ║
╠════════════╬══════╣
║ 2001-10-01 ║ ST ║
║ 2001-10-20 ║ ST ║
║ 2001-11-01 ║ MX ║
║ 2001-11-01 ║ MN ║
║ 2001-11-14 ║ MX ║
║ 2001-11-15 ║ ED ║
║ 2001-11-15 ║ MX ║
║ 2001-11-27 ║ MN ║
║ 2001-12-01 ║ ST ║
║ 2001-12-01 ║ ED ║
║ 2001-12-02 ║ MX ║
║ 2001-12-03 ║ MX ║
║ 2001-12-05 ║ ED ║
║ 2001-12-20 ║ ST ║
║ 2001-12-21 ║ MN ║
║ 2001-12-24 ║ MX ║
║ 2001-12-31 ║ ED ║
╚════════════╩══════╝
我需要:
- 查找从代码为 ST 的日期开始到代码为 ED 的日期结束的任何日期范围。
- 在这些范围内不能有任何以 ST 或 ED 作为代码的元组(范围不能重叠)。
- 无需程序,仅需一个 SELECT 语句(我可以使用 WITH)。
我用以下查询完成了第一部分:
SELECT DISTINCT ON (dt.date) dt.date AS start, dt1.date AS end
FROM dates AS dt, dates AS dt1
WHERE dt.type='ST' AND dt1.type='ED' AND dt.date<dt1.date;
不过我不知道如何消除重叠范围。 使用给定的示例数据我的查询输出:
╔════════════╦════════════╗
║ start ║ end ║
╠════════════╬════════════╣
║ 2001-10-01 ║ 2001-12-01 ║
║ 2001-10-20 ║ 2001-11-15 ║
║ 2001-12-01 ║ 2001-12-31 ║
║ 2001-12-20 ║ 2001-12-31 ║
╚════════════╩════════════╝
如您所见,第二个范围与第一个范围重叠,因此它没有按我的预期工作。
正确的输出应该是:
╔════════════╦════════════╗
║ start ║ end ║
╠════════════╬════════════╣
║ 2001-10-20 ║ 2001-11-15 ║
║ 2001-12-20 ║ 2001-12-31 ║
╚════════════╩════════════╝
如果我理解正确,那么你可以使用lead()
和where
来达到这个目的:
select date as startdate, next_date as enddate
from (select d.*,
lead(code) over (order by date) as next_code,
lead(date) over (order by date) as next_date
from dates d
where code in ('ST', 'ED')
) d
where code = 'ST' and
next_code = 'ED';