Het is meestal een slecht ontwerp om CSV-waarden in één kolom op te slaan. Gebruik in plaats daarvan indien mogelijk een array of een correct genormaliseerd ontwerp.
Terwijl je vastzit in je huidige situatie ...
Voor bekend klein maximum aantal elementen
Een eenvoudige oplossing zonder bedrog of recursie is voldoende:
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 hier
id
zijnde de PK van de originele tabel.
Dit veronderstelt natuurlijk ', ' als scheidingsteken.
Je kunt het gemakkelijk aanpassen.
Gerelateerd:
Voor onbekend aantal elementen
Verschillende manieren. Gebruik in één richting regexp_replace()
om elke vijfde scheidingsteken te vervangen voordat u de nesten verwijdert ...
-- 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 hier
Dit veronderstelt dat het gekozen scheidingsteken ;
nooit verschijnt in uw tekenreeksen. (Net als ,
kan nooit verschijnen.)
Het patroon van de reguliere expressie is de sleutel:'((?:.*?,){4}.*?),'
(?:)
... "niet-vastleggende" set haakjes
()
... "vastleggen" set haakjes *?
... niet-gulzige kwantor
{4}?
... reeks van precies 4 overeenkomsten
De vervanging '\1;'
bevat de terugverwijzing
\1
.
'g'
als vierde functieparameter is vereist voor herhaalde vervanging.
Verder lezen:
- PostgreSQL ®exp_split_to_array + unnest
- Toepassen ` trim()` en `regexp_replace()` op tekstarray
- PostgreSQL unnest() met elementnummer
Andere manieren om dit op te lossen zijn een recursieve CTE of een set-retourfunctie ...
Vul van rechts naar links
(Zoals je hebt toegevoegd in Hoe zet je waarden vanaf de rechterkant in kolommen?
)
Tel gewoon getallen af zoals:
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 hier