Sql 将逗号分隔的值拆分为具有固定列数的目标表
我在Postgres 13.1数据库中有一个只有一列的表。它由许多行组成,行中的值以逗号分隔,最多约20个元素 我想把数据分成多列。但我只有有限数量的列,比如一行中有5个和5个以上的CSV值,所以多余的值必须转移到新的/下一行。如何做到这一点 例如:Sql 将逗号分隔的值拆分为具有固定列数的目标表,sql,postgresql,split,Sql,Postgresql,Split,我在Postgres 13.1数据库中有一个只有一列的表。它由许多行组成,行中的值以逗号分隔,最多约20个元素 我想把数据分成多列。但我只有有限数量的列,比如一行中有5个和5个以上的CSV值,所以多余的值必须转移到新的/下一行。如何做到这一点 例如: a1, b1, c1 a2, b2, c2, d2, e2, f2 a3, b3, c3, d3, e3, f3, g3, h3, i3, j3 a4 a5, b5, c5 ' ' ' 列只有5个,因此输出如下: c1 c2 c3 c4 c5 -
a1, b1, c1
a2, b2, c2, d2, e2, f2
a3, b3, c3, d3, e3, f3, g3, h3, i3, j3
a4
a5, b5, c5
'
'
'
列只有5个,因此输出如下:
c1 c2 c3 c4 c5
---------------
a1 b1 c1
a2 b2 c2 d2 e2
f2
a3 b3 c3 d3 e3
f3 g3 h3 i3 j3
a4
a5 b5 c5
'
'
'
您需要在正在使用的任何后端层中执行此操作 首先,将CSV行转换为字符串数组 然后,使用类似这样的逻辑向数据库添加值
int row = 0; // database row index - can be used to just have a count
final int MAX_COLUMNS = 5;
for(int i = 0; i<rows.length; i++) {
// Convert csv row string to array of each value.
String [] values = rows[i].split(",");
// Dividing whole row into chunks of size of number of columns
for(int j = 0; j < (values.length/(MAX_COLUMNS)) + 1; j++) {
Add Values [MAX_COLUMNS*j,MAX_COLUMNS*j+(MAX_COLUMNS - 1)] to the row [row + j]
row++;
}
}
将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;
小提琴
这假设选择的分隔符;从不出现在字符串中。就像,永远不会出现
正则表达式模式是键:'?:.*?,{4}.*?,'
?:。。。
...
*? ...
{4}? ... 正好4个匹配的序列
替换项“\1;”包含\1
重复更换时需要将“g”作为第四个功能参数
进一步阅读:
解决此问题的其他方法包括递归CTE或集合返回函数
从右向左填充
就像你加进去的
只需倒计时,如:
选择t.id、c.rnk
,拆分部分C.csv5,,,5为c1
,拆分部分C.csv5,,,4为c2
,拆分部分C.csv5,,,3为c3
,拆分部分C.csv5,,,2为c4
,拆分部分C.csv5,,,1为c5
从…起
小提琴
从行到单列
SELECT (ROW_NUMBER() OVER () - 1)/5 AS r, u FROM (SELECT unnest(string_to_array(x,', ')) u from foo) y;
r | u
---+----
0 | a1
0 | b1
0 | c1
0 | a2
0 | b2
1 | c2
1 | d2
...etc
…并返回到已知长度的行
SELECT r,array_agg(u) a FROM (
SELECT (ROW_NUMBER() OVER () - 1)/5 AS r, u FROM (
SELECT unnest(string_to_array(x,', ')) u from foo) y) y1
GROUP BY r ORDER BY r;
r | a
---+------------------
0 | {a1,b1,c1,a2,b2}
1 | {c2,d2,e2,f2,a3}
2 | {b3,c3,d3,e3,f3}
3 | {g3,h3,i3,j3,a4}
4 | {a5,b5,c5}
在此之后,可以使用[]将其插入到表中。如何处理最后一行留给读者作为练习…回答相关问题: 可接受的输出可轻松适应所需的从右到左输出 您只需要更改为拆分部分的顺序。因此,您不返回拆分部分1-5和6-10,而是返回5-1和10-6:
因此,这不是一个代码编写服务。展示你自己如何努力解决这个问题,SO社区会很乐意帮助你找到错误或指导你找到更复杂的解决方案。@funky Hi。好像我没有尝试过使用regexp\u split\u to\u数组、split\u part和其他一两个函数。我是在尝试各种选择后来到这里的。问题是在新行中移动额外的值。我很感激你的想法,但是其他50%的事情没有发生。结果行的顺序重要吗?如果是,怎么做?@ErwinBrandstetter是的,他们的顺序。顺序中没有逻辑部分。Thanks@ErwinBrandstetter你好是否有任何函数或查询将右侧的值放入列中?比如c5下的a1值,c4下的b1值,c3下的c1值?非常感谢您的帮助。问题-在WHERE子句中这意味着什么?虽然我对这个还不熟悉。是否有任何选项可以通过硬编码来实现,比如将来有一行包含20个以上的元素?请帮忙。@Martin2000:。如果第一列不等于空字符串,则该行不是噪声。请看:@Martin2000:I为任意数量的元素添加了解决方案,并从右到左填充。您能在Postgres中告诉我吗?我是新手,不知道你在用什么语法,也不知道如何将你的语法转换成Postgres。你说强逻辑是什么意思?Sry,这个问题我不太清楚。
SELECT r,array_agg(u) a FROM (
SELECT (ROW_NUMBER() OVER () - 1)/5 AS r, u FROM (
SELECT unnest(string_to_array(x,', ')) u from foo) y) y1
GROUP BY r ORDER BY r;
r | a
---+------------------
0 | {a1,b1,c1,a2,b2}
1 | {c2,d2,e2,f2,a3}
2 | {b3,c3,d3,e3,f3}
3 | {g3,h3,i3,j3,a4}
4 | {a5,b5,c5}
SELECT id, 1 AS rnk
, split_part(csv, ', ', 5) AS c1
, split_part(csv, ', ', 4) AS c2
, split_part(csv, ', ', 3) AS c3
, split_part(csv, ', ', 2) AS c4
, split_part(csv, ', ', 1) AS c5
FROM tbl
WHERE split_part(csv, ', ', 1) <> '' -- skip empty rows
UNION ALL
SELECT id, 2
, split_part(csv, ', ', 10)
, split_part(csv, ', ', 9)
, split_part(csv, ', ', 8)
, split_part(csv, ', ', 7)
, split_part(csv, ', ', 6)
FROM tbl
WHERE split_part(csv, ', ', 6) <> '' -- skip empty rows
-- more?
ORDER BY id, rnk;