sql >> Databasteknik >  >> RDS >> PostgreSQL

Dela upp kommaseparerade värden i måltabell med fast antal kolumner

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:

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



  1. Hur man lägger till eller släpper kolumn genom att använda GUI i SQL Server - SQL Server / T-SQL självstudie del 39

  2. varför sql med 'exists' körs långsammare än 'in' med MySQL

  3. Hur man begränsar resultat i Oracle

  4. Hur man återställer MySQL-databas från .myd-, .myi-, .frm-filer