ClickHouse 中按组排列的前 N 行
Top N rows by group in ClickHouse
在 ClickHouse 中按组查询前 N 行的正确方法是什么?
让我们以具有 id2、id4、v3 列且 N=2 的 tbl 为例。
我尝试了以下
SELECT
id2,
id4,
v3 AS v3
FROM tbl
GROUP BY
id2,
id4
ORDER BY v3 DESC
LIMIT 2 BY
id2,
id4
但出现错误
Received exception from server (version 19.3.4):
Code: 215. DB::Exception: Received from localhost:9000, 127.0.0.1. DB::Exception
: Column v3 is not under aggregate function and not in GROUP BY..
我可以将 v3
放入 GROUP BY 中,它似乎有效,但按指标分组效率不高。
有 any
聚合函数,但我们实际上想要 all
值(由 LIMIT BY 子句限制为 2)而不是 any
值,所以听起来不像在这里是妥善的解决方案。
SELECT
id2,
id4,
any(v3) AS v3
FROM tbl
GROUP BY
id2,
id4
ORDER BY v3 DESC
LIMIT 2 BY
id2,
id4
可以这样使用aggregate functions:
SELECT
id2,
id4,
arrayJoin(arraySlice(arrayReverseSort(groupArray(v3)), 1, 2)) v3
FROM tbl
GROUP BY
id2,
id4
您也可以按照本 thread
中所述的“正常”SQL 方式进行操作
虽然 vladimir 的解决方案适用于许多情况,但不适用于我的情况。我有一个 table,看起来像这样:
column | group by
++++++++++++++++++++++
A | Yes
B | Yes
C | No
现在,假设 A 列标识用户,B 列代表用户可以执行的任何操作 e。 G。在您的网站或在线游戏上。 C 列是用户执行此特定操作的频率总和。 Vladimir 的解决方案将允许我获取 A 列和 C 列,但不能获取用户执行的操作(B 列),这意味着我会知道用户执行 某事 的频率,但不知道 什么.
原因是同时按 A 和 B 分组没有意义。每一行都是一个唯一的组,您无法找到前 K 行,因为每个组只有 1成员。结果与您查询的 table 相同。相反,如果您仅按 A 分组,则可以应用 vladimir 的解决方案,但只会得到 A 列和 C 列。您不能输出 B 列,因为它不是 Group By 语句的一部分,如前所述。
如果您想获得用户完成的前 2(或前 5,或前 100)个操作,您可能会寻找这样的解决方案:
SELECT rs.id2, rs.id4, rs.v3
FROM (
SELECT id2, id4, v3, row_number()
OVER (PARTITION BY id2, id4 ORDER BY v3 DESC) AS Rank
FROM tbl
) rs WHERE Rank <= 2
注意:要使用它,您必须设置 allow_experimental_window_functions = 1
。
在 ClickHouse 中按组查询前 N 行的正确方法是什么?
让我们以具有 id2、id4、v3 列且 N=2 的 tbl 为例。
我尝试了以下
SELECT
id2,
id4,
v3 AS v3
FROM tbl
GROUP BY
id2,
id4
ORDER BY v3 DESC
LIMIT 2 BY
id2,
id4
但出现错误
Received exception from server (version 19.3.4):
Code: 215. DB::Exception: Received from localhost:9000, 127.0.0.1. DB::Exception
: Column v3 is not under aggregate function and not in GROUP BY..
我可以将 v3
放入 GROUP BY 中,它似乎有效,但按指标分组效率不高。
有 any
聚合函数,但我们实际上想要 all
值(由 LIMIT BY 子句限制为 2)而不是 any
值,所以听起来不像在这里是妥善的解决方案。
SELECT
id2,
id4,
any(v3) AS v3
FROM tbl
GROUP BY
id2,
id4
ORDER BY v3 DESC
LIMIT 2 BY
id2,
id4
可以这样使用aggregate functions:
SELECT
id2,
id4,
arrayJoin(arraySlice(arrayReverseSort(groupArray(v3)), 1, 2)) v3
FROM tbl
GROUP BY
id2,
id4
您也可以按照本 thread
中所述的“正常”SQL 方式进行操作虽然 vladimir 的解决方案适用于许多情况,但不适用于我的情况。我有一个 table,看起来像这样:
column | group by
++++++++++++++++++++++
A | Yes
B | Yes
C | No
现在,假设 A 列标识用户,B 列代表用户可以执行的任何操作 e。 G。在您的网站或在线游戏上。 C 列是用户执行此特定操作的频率总和。 Vladimir 的解决方案将允许我获取 A 列和 C 列,但不能获取用户执行的操作(B 列),这意味着我会知道用户执行 某事 的频率,但不知道 什么.
原因是同时按 A 和 B 分组没有意义。每一行都是一个唯一的组,您无法找到前 K 行,因为每个组只有 1成员。结果与您查询的 table 相同。相反,如果您仅按 A 分组,则可以应用 vladimir 的解决方案,但只会得到 A 列和 C 列。您不能输出 B 列,因为它不是 Group By 语句的一部分,如前所述。
如果您想获得用户完成的前 2(或前 5,或前 100)个操作,您可能会寻找这样的解决方案:
SELECT rs.id2, rs.id4, rs.v3
FROM (
SELECT id2, id4, v3, row_number()
OVER (PARTITION BY id2, id4 ORDER BY v3 DESC) AS Rank
FROM tbl
) rs WHERE Rank <= 2
注意:要使用它,您必须设置 allow_experimental_window_functions = 1
。