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