Det är vanligtvis dålig design att lagra CSV-värden i en enda kolumn. Om det alls är möjligt, använd en array eller en korrekt normaliserad design istället.
Medan du har fastnat i din nuvarande situation ...
För känt litet maximalt antal element
En enkel lösning utan knep eller rekursioner klarar:
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<>fiol här
id
är PK för den ursprungliga tabellen.
Detta förutsätter naturligtvis ', ' som separator.
Du kan enkelt anpassa dig.
Relaterat:
För okänt antal element
Olika sätt. Ett sätt att använda regexp_replace()
att byta ut var femte avskiljare innan du urrar ...
-- 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<>fiol här
Detta förutsätter att den valda avgränsaren ;
aldrig visas i dina strängar. (Precis som ,
kan aldrig dyka upp.)
Det reguljära uttrycksmönstret är nyckeln:'((?:.*?,){4}.*?),'
(?:)
... ”non-capturing” uppsättning parenteser
()
... “fångar” uppsättning parenteser *?
... icke-girig kvantifierare
{4}?
... sekvens av exakt 4 matchningar
Ersättningen '\1;'
innehåller bakåtreferens
\1
.
'g'
som en fjärde funktionsparameter krävs för upprepad ersättning.
Mer läsning:
- PostgreSQL ®exp_split_unnest
- Ansök ` trim()` och `regexp_replace()` på textarray
- PostgreSQL unnest() med elementnummer
Andra sätt att lösa detta inkluderar en rekursiv CTE eller en setreturnerande funktion ...
Fyll från höger till vänster
(Som du lade till i Hur sätter man in värden som börjar från höger sida i kolumner?
)
Räkna helt enkelt ner siffror som:
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<>fiol här