SELECT id
, min(min_date) AS min_date
, max(max_date) AS max_date
, sum(row_ct) AS row_ct
FROM (
SELECT id, year, min_date, max_date, row_ct
, year - row_number() OVER (PARTITION BY id ORDER BY year) AS grp
FROM (
SELECT id
, extract(year FROM the_date)::int AS year
, min(the_date) AS min_date
, max(the_date) AS max_date
, count(*) AS row_ct
FROM tbl
GROUP BY id, year
) sub1
) sub2
GROUP BY id, grp
ORDER BY id, grp;
1) Group all rows per (id, year
), in subquery sub1
. Record min and max of the date. I added a count of rows (row_ct
) for demonstration.
2) Subtract the row_number()
from the year in the second subquery sub2
. Thus, all rows in succession end up in the same group (grp
). A gap in the years starts a new group.
3) In the final SELECT
, group a second time, this time by (id, grp
) and record min, max and row count again. Voilá. Produces exactly the result you are looking for.
Related answers:
Return array of years as year ranges
Group by repeating attribute