Sqlite group_concat命令

use*_*781 40 sqlite group-concat

在Sqlite中我可以使用group_concat来做:

1...A
1...B
1...C
2...A
2...B
2...C

1...C,B,A
2...C,B,A
Run Code Online (Sandbox Code Playgroud)

但根据文档,连接的顺序是随机的.

我需要将group_concat的输出排序为

1...A,B,C
2...A,B,C
Run Code Online (Sandbox Code Playgroud)

我怎样才能做到这一点?

Adr*_*der 66

你能否在order by子句中使用subselect,然后对这些值进行分组?

就像是

SELECT ID, GROUP_CONCAT(Val)
FROM (
   SELECT ID, Val
   FROM YourTable
   ORDER BY ID, Val
   )
GROUP BY ID;
Run Code Online (Sandbox Code Playgroud)

  • 请注意,这将不适用于MySQL.(没有人说它确实如此,但我错误地认为是这种情况) (7认同)
  • [文档](https://www.sqlite.org/lang_aggfunc.html#groupconcat) 指出:连接元素的顺序是任意的。认为你的答案可以工作,但我怀疑它的顺序是否由文档保证。另请参阅[另一个答案](/sf/answers/1664842301/)。 (4认同)

szm*_*618 9

更准确地说,根据文档

连接元素的顺序是任意的。

这并不意味着随机,它只是意味着开发人员保留使用他们想要的任何顺序的权利,甚至是针对不同查询或不同 SQLite 版本的不同顺序。

对于当前版本,这种排序可能是 Adrian Stander 的答案所暗示的,因为他的代码似乎确实有效。因此,您可能只是通过一些单元测试来保护自己,然后就收工了。但是,如果不仔细检查 SQLite 的源代码,您永远无法 100% 确定这将始终有效。

如果您愿意从源代码构建 SQLite,您也可以尝试编写自己的用户定义聚合函数,但有一个更简单的方法。

幸运的是,从 3.25.0 版本开始,您有了window functions,提供了一个有保证的工作,尽管对您的问题有些难看。

正如您在文档中看到的那样,窗口函数有自己的ORDER BY子句:

在上面的例子中,窗口框架由前一行(“1 PRECEDING”)和下一行(“1 FOLLOWING”)之间的所有行组成,其中行根据 window-defn 中的 ORDER BY 子句进行排序(在本例中为“ORDER BY a”)。

请注意,仅凭这一点并不一定意味着所有聚合函数都遵守窗口框架的排序,但是如果您查看单元测试,您会发现实际上是这样:

do_execsql_test 4.10.1 {
  SELECT a, 
    count() OVER (ORDER BY a DESC),
    group_concat(a, '.') OVER (ORDER BY a DESC) 
  FROM t2 ORDER BY a DESC
} {
  6 1 6
  5 2 6.5
  4 3 6.5.4
  3 4 6.5.4.3
  2 5 6.5.4.3.2
  1 6 6.5.4.3.2.1
  0 7 6.5.4.3.2.1.0
}
Run Code Online (Sandbox Code Playgroud)

所以,总结一下,你可以写

SELECT ID, GROUP_CONCAT(Val) OVER (PARTITION BY ID ORDER BY Val) FROM YourTable;
Run Code Online (Sandbox Code Playgroud)

导致:

1|A
1|A,B
1|A,B,C
2|A
2|A,B
2|A,B,C
Run Code Online (Sandbox Code Playgroud)

不幸的是,它还包含您所需聚合的每个前缀。相反,您希望指定窗口框架始终包含完整范围,然后丢弃冗余值,如下所示:

SELECT DISTINCT ID, GROUP_CONCAT(Val)
OVER (PARTITION BY ID ORDER BY Val ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
FROM YourTable;
Run Code Online (Sandbox Code Playgroud)

或者像这样:

SELECT * FROM (
    SELECT ID, GROUP_CONCAT(Val)
    OVER (PARTITION BY ID ORDER BY Val ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
    FROM YourTable
)
GROUP BY ID;
Run Code Online (Sandbox Code Playgroud)