Normalmente, é um design ruim armazenar valores CSV em uma única coluna. Se possível, use uma matriz ou um design devidamente normalizado.
Enquanto preso com sua situação atual ...
Para um pequeno número máximo de elementos conhecido
Uma solução simples sem truques ou recursão fará:
SELECT id, 1 AS rnk
, split_part(csv, ', ', 1) AS c1
, split_part(csv, ', ', 2) AS c2
, split_part(csv, ', ', 3) AS c3
, split_part(csv, ', ', 4) AS c4
, split_part(csv, ', ', 5) AS c5
FROM tbl
WHERE split_part(csv, ', ', 1) <> '' -- skip empty rows
UNION ALL
SELECT id, 2
, split_part(csv, ', ', 6)
, split_part(csv, ', ', 7)
, split_part(csv, ', ', 8)
, split_part(csv, ', ', 9)
, split_part(csv, ', ', 10)
FROM tbl
WHERE split_part(csv, ', ', 6) <> '' -- skip empty rows
-- three more blocks to cover a maximum "around 20"
ORDER BY id, rnk;
db<>fiddle aqui
id
sendo o PK da tabela original.Isso assume ', ' como separador, obviamente.
Você pode adaptar facilmente.
Relacionado:
Para número desconhecido de elementos
Várias maneiras. Uma maneira de usar
regexp_replace()
para substituir cada quinto separador antes de desaninhar ... -- for any number of elements
SELECT t.id, c.rnk
, split_part(c.csv5, ', ', 1) AS c1
, split_part(c.csv5, ', ', 2) AS c2
, split_part(c.csv5, ', ', 3) AS c3
, split_part(c.csv5, ', ', 4) AS c4
, split_part(c.csv5, ', ', 5) AS c5
FROM tbl t
, unnest(string_to_array(regexp_replace(csv, '((?:.*?,){4}.*?),', '\1;', 'g'), '; ')) WITH ORDINALITY c(csv5, rnk)
ORDER BY t.id, c.rnk;
db<>fiddle aqui
Isso pressupõe que o separador escolhido
;
nunca aparece em suas strings. (Assim como ,
nunca pode aparecer.) O padrão de expressão regular é a chave:
'((?:.*?,){4}.*?),'
(?:)
... conjunto de parênteses "sem captura"
()
... “capturando” conjunto de parênteses *?
... quantificador não-ganancioso
{4}?
... sequência de exatamente 4 partidas A substituição
'\1;'
contém a back-reference
\1
. 'g'
como o quarto parâmetro de função é necessário para substituição repetida. Leitura adicional:
- PostgreSQL e regexp_split_to_array + unnest
- Aplicar ` trim()` e `regexp_replace()` no array de texto
- PostgreSQL unnest() com número de elemento
Outras maneiras de resolver isso incluem um CTE recursivo ou uma função de retorno de conjunto ...
Preencher da direita para a esquerda
(Como você adicionou em Como colocar valores começando do lado direito em colunas? )
Basta contar números como:
SELECT t.id, c.rnk
, split_part(c.csv5, ', ', 5) AS c1
, split_part(c.csv5, ', ', 4) AS c2
, split_part(c.csv5, ', ', 3) AS c3
, split_part(c.csv5, ', ', 2) AS c4
, split_part(c.csv5, ', ', 1) AS c5
FROM ...
db<>fiddle aqui