テスト設定
いくつかのサンプル行を使用して(質問で提供する必要があります):
CREATE TABLE test (
test_id serial PRIMARY KEY
, date1 date NOT NULL
, date2 date NOT NULL
);
INSERT INTO test(date1, date2)
VALUES
('2012-03-20', '2012-01-05') -- 2012-02 lies in between
, ('2012-01-20', '2012-03-05') -- 2012-02 (reversed)
, ('2012-05-06', '2012-05-05') -- nothing
, ('2012-05-01', '2012-06-30') -- still nothing
, ('2012-08-20', '2012-11-05') -- 2012-09 - 2012-10
, ('2012-11-20', '2013-03-05') -- 2012-12 - 2013-02
;
Postgres9.3以降_
LATERAL
結合を使用します:
SELECT to_char(mon, 'YYYY') AS year
, to_char(mon, 'MM') AS month
, count(*) AS ct
FROM (
SELECT date_trunc('mon', least(date1, date2)::timestamp) + interval '1 mon' AS d1
, date_trunc('mon', greatest(date1, date2)::timestamp) - interval '1 mon' AS d2
FROM test
) sub1
, generate_series(d1, d2, interval '1 month') mon -- implicit CROSS JOIN LATERAL
WHERE d2 >= d1 -- exclude ranges without gap right away
GROUP BY mon
ORDER BY mon;
Postgres9.2以前
いいえLATERAL
、まだです。代わりにサブクエリを使用してください。
SELECT to_char(mon, 'YYYY') AS year
, to_char(mon, 'MM') AS month
, count(*) AS ct
FROM (
SELECT generate_series(d1, d2, interval '1 month') AS mon
FROM (
SELECT date_trunc('mon', least(date1, date2)::timestamp) + interval '1 mon' AS d1
, date_trunc('mon', greatest(date1, date2)::timestamp) - interval '1 mon' AS d2
FROM test
) sub1
WHERE d2 >= d1 -- exclude ranges without gap right away
) sub2
GROUP BY mon
ORDER BY mon;
結果
year | month | ct
------+-------+----
2012 | 2 | 2
2012 | 9 | 1
2012 | 10 | 1
2012 | 12 | 1
2013 | 1 | 1
2013 | 2 | 1
db<>fiddleここで
SQLFiddle。
説明
2つの日付の間の完全な暦月を探しています。
これらのクエリは、日付またはタイムスタンプを昇順または降順で処理し、正常に機能するはずです。
start> endの場合、行を返さないため、このWHERE
句はオプションです。generate_series()
ただし、空の範囲を事前に除外する方が少し速いはずです。
timestamp
それを少しきれいにそしてより速くするためのキャスト。理論的根拠: