46

在 Sqlite 我可以使用 group_concat 来做:

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

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

但连接的顺序是随机的 - 根据文档。

我需要将 group_concat 的输出排序为

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

我怎样才能做到这一点?

4

3 回答 3

71

您不能使用带有 order by 子句的子选择,然后对值进行分组吗?

就像是

SELECT ID, GROUP_CONCAT(Val)
FROM (
   SELECT ID, Val
   FROM YourTable
   ORDER BY ID, Val
   )
GROUP BY ID;
于 2009-12-13T19:02:58.577 回答
11

更准确地说,根据文档

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

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

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

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

幸运的是,从 3.25.0 版开始,您有了窗口函数,提供了一个保证工作的方法,尽管您的问题的解决方案有些丑陋。

正如您在文档中看到的那样,窗口函数有自己的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
}

所以,总结起来,你可以写

SELECT ID, GROUP_CONCAT(Val) OVER (PARTITION BY ID ORDER BY Val) FROM YourTable;

导致:

1|A
1|A,B
1|A,B,C
2|A
2|A,B
2|A,B,C

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

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

或像这样:

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;
于 2019-07-17T13:09:29.523 回答
0

偶然发现了潜在的排序问题,我尝试了这个:(...在 10.4.18-MariaDB 上)

select GROUP_CONCAT(ex.ID) as ID_list
FROM (
SELECT usr.ID
FROM (
SELECT u1.ID as ID
FROM table_users u1
) usr
GROUP BY ID
) ex

...并找到了已排序的序列化 ID_list!但我没有解释这个现在“正确”(?)的结果。

于 2021-03-29T10:43:56.020 回答