In genere è una cattiva progettazione archiviare i valori CSV in una singola colonna. Se possibile, usa invece un array o un design adeguatamente normalizzato.
Mentre sei bloccato con la tua situazione attuale ...
Per un numero massimo di elementi noto piccolo
Una soluzione semplice senza inganno o ricorsione andrà bene:
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<>violino qui
id
essendo il PK della tabella originale.
Questo presuppone ',' come separatore, ovviamente.
Puoi adattarti facilmente.
Correlati:
Per numero sconosciuto di elementi
Vari modi. Uso unidirezionale regexp_replace()
sostituire ogni quinto separatore prima di disnidare...
-- 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<>violino qui
Ciò presuppone che il separatore scelto ;
mai appare nelle tue stringhe. (Proprio come ,
non può mai apparire.)
Il modello di espressione regolare è la chiave:'((?:.*?,){4}.*?),'
(?:)
... Set di parentesi "non catturanti"
()
... insieme di parentesi "cattura" *?
... quantificatore non avido
{4}?
... sequenza di esattamente 4 corrispondenze
La sostituzione '\1;'
contiene il back-reference
\1
.
'g'
poiché il quarto parametro di funzione è richiesto per la sostituzione ripetuta.
Ulteriori letture:
- PostgreSQL e regexp_split_to_array + unnest
- Applica ` trim()` e `regexp_replace()` sull'array di testo
- PostgreSQL unnest() con numero elemento
Altri modi per risolvere questo problema includono un CTE ricorsivo o una funzione di ritorno di set ...
Riempi da destra a sinistra
(Come hai aggiunto in Come inserire i valori a partire dal lato destro nelle colonne?
)
Conta semplicemente alla rovescia numeri come:
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<>violino qui