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
Produce exactamente el resultado deseado.
Si trata con una matriz de varchar (varchar[]
, simplemente cámbielo a int[]
, antes de continuar. Parece estar en forma perfectamente legal para eso:
years::int[]
Reemplace la subselección interna con el nombre de su tabla fuente en código productivo.
FROM (VALUES (2::int, '{1999,2000,2010,2011,2012}'::int[])
,(3, '{1990,1991,2007}')
) AS tbl(id, years)
->
FROM tbl
Ya que estamos tratando con un número naturalmente ascendente (el año) podemos usar un atajo para formar grupos de años consecutivos (formando un rango). Resto el año en sí del número de fila (ordenado por año). Para años consecutivos, tanto el número de fila como el año aumentan en uno y producen el mismo grp
número. De lo contrario, comienza un nuevo rango.
Más sobre funciones de ventana en el manual aquí y aquí .
Una función plpgsql podría ser incluso más rápida en este caso. Tendrías que probar. Ejemplos en estas respuestas relacionadas:
Recuento ordenado de repeticiones/duplicados consecutivos
ROW_NUMBER() muestra valores inesperados