Обикновено е лош дизайн да се съхраняват CSV стойности в една колона. Ако изобщо е възможно, вместо това използвайте масив или правилно нормализиран дизайн.
Докато сте в текущата си ситуация...
За известен малък максимален брой елементи
Едно просто решение без хитрост или рекурсия ще свърши работа:
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 тук
id
като PK на оригиналната таблица.
Това очевидно предполага ', ' като разделител.
Можете да се адаптирате лесно.
Свързани:
За неизвестен брой елементи
Различни начини. Един начин използвайте regexp_replace()
за замяна на всеки пети разделител преди разместване ...
-- 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 тук
Това предполага, че избраният разделител ;
никога се появява във вашите низове. (Точно като ,
никога не може да се появи.)
Моделът на регулярния израз е ключът:'((?:.*?,){4}.*?),'
(?:)
... "неприхващащ" набор от скоби
()
... „улавяне“ на набор от скоби *?
... неалчен квантификатор
{4}?
... поредица от точно 4 съвпадения
Замяната '\1;'
съдържа обратна препратка
\1
.
'g'
като четвърти функционален параметър е необходим за многократна подмяна.
Допълнителна информация:
- PostgreSQL ®exp_split_to_array + unnest
- Прилагане ` trim()` и `regexp_replace()` върху текстов масив
- PostgreSQL unnest() с номер на елемент
Други начини за решаване на това включват рекурсивен CTE или функция за връщане на набор ...
Попълнете отдясно наляво
(Както добавихте в Как да поставяте стойности, започващи от дясната страна, в колони?
)
Просто отброявайте числа като:
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 тук