【问题标题】:SQL: How to concatenate every group of N rows into a single rowSQL:如何将每组 N 行连接成一行
【发布时间】:2019-10-26 05:02:09
【问题描述】:

我有这个

 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

澄清:

  • 我想要一个类似 vanilla 的 SQL 解决方案(所以它可以在 mysql、sybase、oracle 和 postgres 中工作)

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

  • 我在这个基础上没有写权限,只能读

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

  • 如果某些组的大小不完全是 N 也没关系(如最后一个)

【问题讨论】:

    标签: sql string-aggregation


    【解决方案1】:

    惊人的脚本。但我怀疑你遗漏了一个 AS。所以我把它变成了这样:

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

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

    按转换分组((seqnum - 1) / 3 as int);

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

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

    选择 string_agg(t.[Student Name], ';') as [All Names], string_agg(t.[Student Email], ';') as [All Emails]

    来自 ( SELECT [Student Name], [Student Email], ROW_NUMBER() OVER (ORDER BY [Student Email]) AS RowNo 来自 [课程邮件列表] 其中 [产品名称]='在线课程' ) 作为吨 按演员分组((RowNo - 1)/ 100 as int);

    希望对你有帮助

    【讨论】:

    • 请理解答案部分是为了回答。如果您有答案,请发布一个包含所有信息的独立答案。 不要将您尝试的答案拆分为多个答案。这不是堆栈溢出的工作方式。你的帖子很混乱,因为它们被分解了,但它看起来更像是你有一个问题,所以请发布一个新问题,再次独立。如果你认为这篇文章提供了上下文,你也可以链接到这篇文章。
    • 另外,请查看formatting help page 以改进您的格式。
    • 这没有提供问题的答案。一旦你有足够的reputation,你就可以comment on any post;相反,provide answers that don't require clarification from the asker。 - From Review
    【解决方案2】:

    标准 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(),所以你必须使用+。这会为分隔符生成; 而不是,,但它非常接近。

    【讨论】:

    • 哇,感谢您的快速响应。我已经阅读了很多关于 sybase 的内容,据我了解,您需要一些自定义过程...
    猜你喜欢
    • 1970-01-01
    • 2020-03-01
    • 2019-08-31
    • 1970-01-01
    • 1970-01-01
    • 2021-05-06
    • 1970-01-01
    • 2017-09-13
    • 1970-01-01
    相关资源
    最近更新 更多