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
Produz exatamente o resultado desejado.
Se você lida com um array de varchar (
varchar[]
, basta convertê-lo para int[]
, antes de prosseguir. Parece estar em forma perfeitamente legal para isso:years::int[]
Substitua a sub-seleção interna pelo nome da sua tabela de origem no código produtivo.
FROM (VALUES (2::int, '{1999,2000,2010,2011,2012}'::int[])
,(3, '{1990,1991,2007}')
) AS tbl(id, years)
->
FROM tbl
Como estamos lidando com um número naturalmente crescente (o ano) podemos usar um atalho para formar grupos de anos consecutivos (formando um intervalo). Eu subtraio o próprio ano do número da linha (ordenado por ano). Para anos consecutivos, o número da linha e o ano são incrementados em um e produzem o mesmo
grp
número. Caso contrário, um novo intervalo é iniciado. Mais sobre funções da janela no manual aqui e aqui .
Uma função plpgsql pode ser ainda mais rápida neste caso. Você teria que testar. Exemplos nestas respostas relacionadas:
Contagem ordenada de repetições/duplicatas consecutivas
ROW_NUMBER() mostra valores inesperados