Sql 将逗号分隔的值拆分为具有固定列数的目标表

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 -

我在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
'
'
'

您需要在正在使用的任何后端层中执行此操作

首先,将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;