SELECT id, string_agg(year_range, ', ') AS year_ranges
FROM (
SELECT id, CASE WHEN count(*) > 1
THEN min(year)::text || '-' || max(year)::text
ELSE min(year)::text
END AS year_range
FROM (
SELECT *, row_number() OVER (ORDER BY id, year) - year AS grp
FROM (
SELECT id, unnest(years) AS year
FROM (VALUES (2::int, '{1999,2000,2010,2011,2012}'::int[])
,(3, '{1990,1991,2007}')
) AS tbl(id, years)
) sub1
) sub2
GROUP BY id, grp
ORDER BY id, min(year)
) sub3
GROUP BY id
ORDER BY id
Produziert genau das gewünschte Ergebnis.
Wenn Sie mit einem Array von varchar (varchar[]
, wandeln Sie es einfach in int[]
um , bevor Sie fortfahren. Es scheint dafür eine vollkommen legale Form zu haben:
years::int[]
Ersetzen Sie den inneren Sub-Select durch den Namen Ihrer Quelltabelle im Produktivcode.
FROM (VALUES (2::int, '{1999,2000,2010,2011,2012}'::int[])
,(3, '{1990,1991,2007}')
) AS tbl(id, years)
->
FROM tbl
Da wir es mit einer natürlich aufsteigenden Zahl zu tun haben (das Jahr) können wir eine Abkürzung verwenden, um Gruppen aufeinanderfolgender Jahre zu bilden (die einen Bereich bilden). Ich subtrahiere das Jahr selbst von der Zeilennummer (sortiert nach Jahr). Für aufeinanderfolgende Jahre erhöhen sich sowohl die Zeilennummer als auch das Jahr um eins und erzeugen dasselbe grp
Nummer. Andernfalls beginnt ein neuer Bereich.
Mehr zu Fensterfunktionen im Handbuch hier und hier .
Eine plpgsql-Funktion könnte in diesem Fall sogar noch schneller sein. Müsste man testen. Beispiele in diesen verwandten Antworten:
Bestellte Anzahl aufeinanderfolgender Wiederholungen / Duplikate
ROW_NUMBER() zeigt unerwartete Werte