This query seems to generate what you are looking for, using the "filter" table you have defined on sqlfiddle. It doesn't include "not exist" rows. I'm not sure if you really wanted that or if you just wanted to show that it doesn't exist. I assume the latter. Otherwise some additional periods from the filter table will need to be "unioned" in I guess.
The idea is to first create periods like you were trying to do with "lag", but use "lead" to say the end of this period is the start of the next period. Probably want to subtract 1 day from the lead to make the end date non-inclusive, but I didn't want to convolute this more.
- If there is no end period, use the filter end period (coalesce)
- Any start dates less then the filter start date is bumped up to the filter start date (greatest)
- Any end dates greater than the filter end date are reduced to the filter end date (least)
Query:
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;
Setup:
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);
Run:
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=#