将 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小提琴here
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小提琴here
这假定所选的分隔符 ; 从不 出现在您的字符串中。 (就像,永远不会出现。)
正则表达式模式是关键:'((?:.*?,){4}.*?),'
(?:) ... “non-capturing” set of parentheses
() ... “capturing” set of parentheses
*? ... non-greedy quantifier
{4}? ... 正好 4 个匹配的序列
替换 '\1;' 包含 back-reference \1。
'g'作为第四个函数参数需要重复替换。
进一步阅读:
解决此问题的其他方法包括递归 CTE 或集合返回函数 ...
从右到左填充
(就像您在 How to put values starting from the right side into columns? 中添加的一样)
只需倒数如下数字:
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小提琴here