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
Produkuje přesně požadovaný výsledek.
Pokud se zabýváte polem varchar (varchar[]
, stačí jej přenést do int[]
, než budete pokračovat. Zdá se, že je v naprosto legální formě:
years::int[]
Nahraďte vnitřní podvýběr názvem vaší zdrojové tabulky v produktivním kódu.
FROM (VALUES (2::int, '{1999,2000,2010,2011,2012}'::int[])
,(3, '{1990,1991,2007}')
) AS tbl(id, years)
->
FROM tbl
Protože máme co do činění s přirozeně stoupajícím číslem (rok) můžeme použít zkratku k vytvoření skupin po sobě jdoucích let (tvořících rozsah). Od čísla řádku odečítám samotný rok (seřazeno podle roku). V po sobě jdoucích letech se číslo řádku i rok zvýší o jednu a vytvoří stejný grp
číslo. Jinak začíná nový rozsah.
Více o funkcích oken v příručce zde a zde .
Funkce plpgsql může být v tomto případě ještě rychlejší. Musel bys testovat. Příklady v těchto souvisejících odpovědích:
Seřazený počet po sobě jdoucích opakování / duplikátů
ROW_NUMBER() zobrazuje neočekávané hodnoty