Il est généralement mal conçu de stocker les valeurs CSV dans une seule colonne. Si possible, utilisez plutôt un tableau ou une conception correctement normalisée.
Bien que coincé avec votre situation actuelle ...
Pour un petit nombre maximum d'éléments connu
Une solution simple sans ruse ni récursivité fera l'affaire :
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<>violon ici
id
étant le PK de la table d'origine.
Cela suppose ', ' comme séparateur, évidemment.
Vous pouvez vous adapter facilement.
Connexe :
Pour un nombre inconnu d'éléments
Différentes manières. Une façon d'utiliser regexp_replace()
pour remplacer un séparateur sur cinq avant de désimbriquer...
-- 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<>violon ici
Cela suppose que le séparateur choisi ;
jamais apparaît dans vos chaînes. (Tout comme ,
ne peut jamais apparaître.)
Le modèle d'expression régulière est la clé :'((?:.*?,){4}.*?),'
(?:)
... ensemble de parenthèses "sans capture"
()
... "capturer" un ensemble de parenthèses *?
... quantificateur non gourmand
{4}?
... suite d'exactement 4 correspondances
Le remplacement '\1;'
contient la back-reference
\1
.
'g'
comme quatrième paramètre de fonction est requis pour un remplacement répété.
Lectures complémentaires :
- PostgreSQL ®exp_split_to_array + unnest
- Appliquer ` trim()` et `regexp_replace()` sur le tableau de texte
- PostgreSQL unnest() avec le numéro d'élément
D'autres façons de résoudre ce problème incluent un CTE récursif ou une fonction de retour d'ensemble ...
Remplir de droite à gauche
(Comme vous l'avez ajouté dans Comment mettre des valeurs en partant du côté droit dans des colonnes ?
)
Comptez simplement des nombres comme :
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<>violon ici