Por lo general, es un mal diseño almacenar valores CSV en una sola columna. Si es posible, use una matriz o un diseño correctamente normalizado en su lugar.
Mientras estás atrapado en tu situación actual...
Para un número máximo de elementos pequeño conocido
Una solución simple sin engaños ni recursividad servirá:
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 aquí
id
siendo el PK de la tabla original.
Esto asume ',' como separador, obviamente.
Puede adaptarse fácilmente.
Relacionado:
Para número desconocido de elementos
Varias maneras. Una forma de usar regexp_replace()
para reemplazar cada quinto separador antes de anidar...
-- 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 aquí
Esto supone que el separador elegido ;
nunca aparece en sus cadenas. (Al igual que ,
nunca puede aparecer.)
El patrón de expresión regular es la clave:'((?:.*?,){4}.*?),'
(?:)
... conjunto de paréntesis "sin captura"
()
... conjunto de paréntesis de “captura” *?
... cuantificador no codicioso
{4}?
... secuencia de exactamente 4 coincidencias
El reemplazo '\1;'
contiene la referencia inversa
\1
.
'g'
como cuarto parámetro de función se requiere para el reemplazo repetido.
Lectura adicional:
- PostgreSQL y regexp_split_to_array + unnest
- Aplicar ` trim()` y `regexp_replace()` en la matriz de texto
- PostgreSQL unnest() con número de elemento
Otras formas de resolver esto incluyen un CTE recursivo o una función de devolución de conjuntos...
Rellenar de derecha a izquierda
(Como agregaste en ¿Cómo poner valores comenzando desde el lado derecho en columnas?
)
Simplemente cuenta hacia atrás 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 aquí