sql >> Base de Datos >  >> RDS >> PostgreSQL

Dividir valores separados por comas en la tabla de destino con un número fijo de columnas

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:

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í