このクエリは、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=#