Edited* Using psql 9.4.26
I have this table:
Key | Start | End |
---|---|---|
ABC123 | 24/01/2012 | 23/01/2013 |
And using psql, I'm trying to get an output similar to:
Key | Start | End |
---|---|---|
ABC123 | 24/01/2012 | 31/01/2012 |
ABC123 | 1/02/2012 | 29/02/2012 |
ABC123 | 1/03/2012 | 31/03/2012 |
ABC123 | 1/04/2012 | 30/04/2012 |
ABC123 | 1/05/2012 | 31/05/2012 |
ABC123 | 1/06/2012 | 30/06/2012 |
ABC123 | 1/02/2012 | 31/07/2012 |
ABC123 | 1/08/2012 | 31/08/2012 |
ABC123 | 1/09/2012 | 30/09/2012 |
ABC123 | 1/10/2012 | 31/10/2012 |
ABC123 | 1/11/2012 | 30/11/2012 |
ABC123 | 1/12/2012 | 31/12/2012 |
ABC123 | 1/01/2013 | 23/01/2013 |
I queried the following:
WITH date_ranges AS (
SELECT
TO_DATE("Start", 'DD/MM/YYYY') AS start_date,
TO_DATE("End", 'DD/MM/YYYY') AS end_date
FROM
tabl
)
SELECT
TO_CHAR(month_start, 'DD/MM/YYYY') AS Start,
TO_CHAR(LEAST(month_start + INTERVAL '1 month' - INTERVAL '1 day', end_date), 'DD/MM/YYYY') AS End
FROM
date_ranges
CROSS JOIN LATERAL generate_series(
date_trunc('month', start_date),
LEAST(date_trunc('month', end_date) + INTERVAL '1 month' - INTERVAL '1 day', end_date),
INTERVAL '1 month'
) AS month_start(month_start);
However, the first record output is this:
Key | Start | End |
---|---|---|
ABC123 | 1/01/2012 | 31/01/2012 |
The outcome should be:
Key | Start | End |
---|---|---|
ABC123 | 24/01/2012 | 31/01/2012 |
Answers
To achieve the desired output where the start date begins from the actual start date in the table rather than the first day of the month, you can adjust the generate_series
function accordingly. Here's the modified query:
WITH date_ranges AS (
SELECT
TO_DATE("Start", 'DD/MM/YYYY') AS start_date,
TO_DATE("End", 'DD/MM/YYYY') AS end_date
FROM
tabl
)
SELECT
TO_CHAR(start_date, 'DD/MM/YYYY') AS Start,
TO_CHAR(LEAST(start_date + INTERVAL '1 month' - INTERVAL '1 day', end_date), 'DD/MM/YYYY') AS End
FROM
date_ranges
UNION ALL
SELECT
TO_CHAR(month_start, 'DD/MM/YYYY') AS Start,
TO_CHAR(LEAST(month_start + INTERVAL '1 month' - INTERVAL '1 day', end_date), 'DD/MM/YYYY') AS End
FROM
date_ranges
CROSS JOIN LATERAL generate_series(
date_trunc('month', start_date + INTERVAL '1 month'),
LEAST(date_trunc('month', end_date) + INTERVAL '1 month' - INTERVAL '1 day', end_date),
INTERVAL '1 month'
) AS month_start(month_start);
In this modified query:
- I've added a
UNION ALL
clause to include the initial period from the actual start date to the end of the first month. - The
generate_series
function now starts from the beginning of the second month onwards to ensure that the first period starts from the actual start date in the table. - The
start_date
is used as the start date for the first period instead of the truncated month.