此查询似乎生成了您正在寻找的内容,使用您在 sqlfiddle 上定义的“过滤器”表。它不包括“不存在”行。我不确定你是否真的想要那个,或者你只是想表明它不存在。我假设后者。否则我猜过滤器表中的一些额外时间段将需要“联合”。
这个想法是首先创建一个时期,就像你试图用“滞后”做的那样,但是用“领先”来表示这个时期的结束是下一个时期的开始。可能想从领先中减去 1 天以使结束日期不包含在内,但我不想让这个更复杂。
- 如果没有结束期,则使用过滤器结束期(合并)
- 任何早于过滤器开始日期的开始日期都将上升到过滤器开始日期(最大)
- 任何大于过滤器结束日期的结束日期都会减少到过滤器结束日期(最小)
询问:
SELECT id, doc, status, from_date, to_date
FROM ( SELECT id, doc, status, GREATEST(d.date, f.start_date) AS from_date
,LEAST( COALESCE( lead(date) OVER (PARTITION BY doc ORDER BY date)
,f.end_date
)
,f.end_date ) AS to_date
FROM documents d
,filter f
) d
WHERE from_date < to_date
ORDER BY doc, from_date;
设置:
CREATE TABLE documents(id int, doc int, date date, status varchar (1));
insert into documents values(1, 11, to_date('2012-01-01', 'yyyy-mm-dd'),'A');
insert into documents values(2, 11, to_date('2012-04-01', 'yyyy-mm-dd'),'I');
insert into documents values(3, 11, to_date('2012-04-25', 'yyyy-mm-dd'),'A');
insert into documents values(4, 11, to_date('2012-06-01', 'yyyy-mm-dd'),'I');
insert into documents values(5, 22, to_date('2012-04-18', 'yyyy-mm-dd'),'A');
insert into documents values(6, 22, to_date('2012-04-30', 'yyyy-mm-dd'),'I');
CREATE TABLE filter(start_date date, end_date date);
跑:
postgres=# insert into filter values(to_date('2012-02-03', 'yyyy-mm-dd'), to_date('2012-05-05', 'yyyy-mm-dd'));
INSERT 0 1
postgres=# SELECT id, doc, status, from_date, to_date
postgres-# FROM ( SELECT id, doc, status, GREATEST(d.date, f.start_date) AS from_date
postgres(# ,LEAST( COALESCE( lead(date) OVER (PARTITION BY doc ORDER BY date)
postgres(# ,f.end_date
postgres(# )
postgres(# ,f.end_date ) AS to_date
postgres(# FROM documents d
postgres(# ,filter f
postgres(# ) d
postgres-# WHERE from_date < to_date
postgres-# ORDER BY doc, from_date
postgres-# ;
id | doc | status | from_date | to_date
----+-----+--------+------------+------------
1 | 11 | A | 2012-02-03 | 2012-04-01
2 | 11 | I | 2012-04-01 | 2012-04-25
3 | 11 | A | 2012-04-25 | 2012-05-05
5 | 22 | A | 2012-04-18 | 2012-04-30
6 | 22 | I | 2012-04-30 | 2012-05-05
(5 rows)
postgres=# truncate table filter;
TRUNCATE TABLE
postgres=# insert into filter values(to_date('2012-01-02', 'yyyy-mm-dd'), to_date('2012-02-28', 'yyyy-mm-dd'));
INSERT 0 1
postgres=# SELECT id, doc, status, from_date, to_date
postgres-# FROM ( SELECT id, doc, status, GREATEST(d.date, f.start_date) AS from_date
postgres(# ,LEAST( COALESCE( lead(date) OVER (PARTITION BY doc ORDER BY date)
postgres(# ,f.end_date
postgres(# )
postgres(# ,f.end_date ) AS to_date
postgres(# FROM documents d
postgres(# ,filter f
postgres(# ) d
postgres-# WHERE from_date < to_date
postgres-# ORDER BY doc, from_date;
id | doc | status | from_date | to_date
----+-----+--------+------------+------------
1 | 11 | A | 2012-01-02 | 2012-02-28
(1 row)
postgres=# truncate table filter;
TRUNCATE TABLE
postgres=# insert into filter values(to_date('2012-04-18', 'yyyy-mm-dd'), to_date('2012-04-20', 'yyyy-mm-dd'));
INSERT 0 1
postgres=# SELECT id, doc, status, from_date, to_date
postgres-# FROM ( SELECT id, doc, status, GREATEST(d.date, f.start_date) AS from_date
postgres(# ,LEAST( COALESCE( lead(date) OVER (PARTITION BY doc ORDER BY date)
postgres(# ,f.end_date
postgres(# )
postgres(# ,f.end_date ) AS to_date
postgres(# FROM documents d
postgres(# ,filter f
postgres(# ) d
postgres-# WHERE from_date < to_date
postgres-# ORDER BY doc, from_date;
id | doc | status | from_date | to_date
----+-----+--------+------------+------------
2 | 11 | I | 2012-04-18 | 2012-04-20
5 | 22 | A | 2012-04-18 | 2012-04-20
(2 rows)
postgres=#