SQL:如何将每组N行连接成一行

时间:2019-06-11 17:58:51

标签: sql concatenation

我有这个

 ID | Name 
----+-------
 31 | Abby
 24 | Bruce 
 44 | Carl 
 49 | Derek
 55 | Eric
 81 | Fred

我想将N行的组连接成一行。对于N = 3,这会给我这个

    ID    |    Name 
----------+----------------
 31,24,44 | Abby,Bruce,Carl
 49,55,81 | Derek,Eric,Fred

我设法生成一行以使用GROUP BY和CONCAT,但是它仅在mysql中有效...

SET @row_number = 0;
SELECT *, (@row_number:=@row_number + 1) AS r1, (@row_number - 1) DIV 3 AS r2 FROM table1

 ID | Name  | r1| r2
----+-------+---+---
 31 | Abby  | 1 | 0
 24 | Bruce | 2 | 0
 44 | Carl  | 3 | 0
 49 | Derek | 4 | 1
 55 | Eric  | 5 | 1
 81 | Fred  | 6 | 1

为澄清起见:

  • 我想要一个类似香草的SQL解决方案(因此它将在mysql,sybase,oracle和postgres中工作)

  • 我不需要任何命令,我只想在某个时候重构原始表

  • 在此基础上,我没有写权限,只能阅读

  • 我想连接任何列类型(通过将它们强制转换为字符串)并处理NULL

  • 如果某些组的大小不完全是N(像最后一个一样)也可以

2 个答案:

答案 0 :(得分:1)

标准的SQL解决方案如下所示:

select listagg(id, ',') within group (order by id) as ids,
       listagg(name, ',') within group (order by id) as names
from (select t.*, row_number() over (order by id) as seqnum
      from t
     ) t
group by cast( (seqnum - 1) / 3 as int);

我认为这将在Oracle中按原样工作。在MySQL中,您需要将listagg()更改为group_concat()(并使用MySQL 8+),而在Postgres中,您需要将listagg()更改为string_agg()

而且,在Sybase中几乎很难做到这一点。

哦,等等,还有另一种方法:

select concat( (case when seqnum % 3 = 1 then concat(id, ';') else '' end),
               (case when seqnum % 3 = 2 then concat(id, ';') else '' end),
               (case when seqnum % 3 = 0 then concat(id, ';') else '' end)
             ) as ids,
       concat( (case when seqnum % 3 = 1 then concat(name, ';') else '' end),
               (case when seqnum % 3 = 2 then concat(name, ';') else '' end),
               (case when seqnum % 3 = 0 then concat(name, ';') else '' end)
             ) as name           
from (select t.*, row_number() over (order by id) as seqnum
      from t
     ) t
group by cast( (seqnum - 1) / 3 as int);

当然,Sybase不支持concat(),因此必须使用+。这会为分隔符而不是;产生,,但是非常接近。

答案 1 :(得分:0)

惊人的脚本。但我怀疑你遗漏了一个 AS。所以我是这样做的:

选择 string_agg(t.[id], ',') 作为 ID, string_agg(t.[name], ',') 作为名称 来自

(select t.*, row_number() over (order by id) as seqnum 来自 [表名] ) 作为 t

group by cast((seqnum - 1) / 3 as int);

就我而言,就是这样(尽管我无法让“组内(按 ID 排序)”以任何方式工作......嗯)

这是我的效果很好,它是发给我所有学生的电子邮件列表,每 100 行合并成一行。遗憾的是,String_Agg 将其限制为 8000 个字符。有人知道 SQL Server 的 String_Agg 的替代品吗?

选择 string_agg(t.[Student Name], ';') as [All Names], string_agg(t.[Student Email], ';') as [所有电子邮件]

来自 ( SELECT [Student Name], [Student Email], ROW_NUMBER() OVER (ORDER BY [Student Email]) AS RowNo FROM [Mailing List For Courses] where [Prod Name]='Online Courses' ) AS t group by cast((RowNo - 1) / 100 as int);

希望对您有所帮助<3