Es ist normalerweise ein schlechtes Design, CSV-Werte in einer einzigen Spalte zu speichern. Verwenden Sie nach Möglichkeit stattdessen ein Array oder ein ordnungsgemäß normalisiertes Design.
Während Sie in Ihrer aktuellen Situation feststecken ...
Für bekannte kleine maximale Anzahl von Elementen
Eine einfache Lösung ohne Tricks oder Rekursion reicht aus:
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
ist die PK der Originaltabelle.
Dies setzt natürlich ', ' als Trennzeichen voraus.
Sie können es leicht anpassen.
Verwandte:
Für unbekannte Anzahl von Elementen
Verschiedene Wege. Verwenden Sie zum einen regexp_replace()
um jedes fünfte Trennzeichen vor dem Aufheben der Verschachtelung zu ersetzen ...
-- 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
Dies setzt voraus, dass das gewählte Trennzeichen ;
nie erscheint in Ihren Saiten. (Genau wie ,
kann niemals erscheinen.)
Das reguläre Ausdrucksmuster ist der Schlüssel:'((?:.*?,){4}.*?),'
(?:)
... "nicht erfassender" Klammersatz
()
... "erfassender" Klammersatz *?
... nicht-gieriger Quantifizierer
{4}?
... Folge von genau 4 Treffern
Der Ersatz '\1;'
enthält die Rückreferenz
\1
.
'g'
als vierter Funktionsparameter wird zum wiederholten Ersetzen benötigt.
Weiterführende Literatur:
- PostgreSQL ®exp_split_to_array + unnest
- Apply ` trim()` und `regexp_replace()` auf Textarray
- PostgreSQL-unnest() mit Elementnummer
Andere Möglichkeiten, dies zu lösen, umfassen einen rekursiven CTE oder eine Set-Returning-Funktion ...
Von rechts nach links füllen
(Wie Sie in Wie setze ich Werte beginnend von der rechten Seite in Spalten?
hinzugefügt )
Zählen Sie einfach Zahlen herunter wie:
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