如何按不同的值将 clickhouse 中的 (value,count) 数组分组?

Posted

技术标签:

【中文标题】如何按不同的值将 clickhouse 中的 (value,count) 数组分组?【英文标题】:How can I group by distinct value into a (value,count) array in clickhouse? 【发布时间】:2020-03-27 09:19:43 【问题描述】:

例如,我有一张桌子 A

create table A (
    id Int64,
    discrete1 String
    discrete2 String
) engine=Log;

还有一些数据

insert into A values
(1,'A','a')
(1,'B','b')
(1,'A','c')
(2,'C','a')
(1,'A','a');

如何选择这个结果,元组是(value,count)

1,[(A,3),(B,1)],[(a,2),(b,1),(c,1)]
2,[(C,1)],[(a,1)]

我的表可能有许多离散值列,有什么方法可以在一次选择中做到这一点,而无需逐个按离散值列分组。

【问题讨论】:

【参考方案1】:
SELECT
    id,
    arrayMap((x, y) -> (x, y),
      (arrayReduce('sumMap', [(groupArrayArray([discrete1]) as arrdiscrete1)], 
              [arrayResize(CAST([], 'Array(UInt64)'), length(arrdiscrete1), toUInt64(1))]) as sdiscrete1).1,
      sdiscrete1.2) rdiscrete1,
    arrayMap((x, y) -> (x, y),
      (arrayReduce('sumMap', [(groupArrayArray([discrete2]) as arrdiscrete2)], 
              [arrayResize(CAST([], 'Array(UInt64)'), length(arrdiscrete2), toUInt64(1))]) as sdiscrete2).1,
      sdiscrete2.2) rdiscrete2
FROM A
GROUP BY id

┌─id─┬─rdiscrete1────────┬─rdiscrete2────────────────┐
│  2 │ [('C',1)]         │ [('a',1)]                 │
│  1 │ [('A',3),('B',1)] │ [('a',2),('b',1),('c',1)] │
└────┴───────────────────┴───────────────────────────┘

【讨论】:

运行查询时出错: Received exception from server (version 19.17.2): Code: 43. DB::Exception: Received from clickhouse-server:9000. DB::Exception: 聚合函数 sumMap 的参数类型非法。 因为strings , v20.3.2.1, 2020-03-12 为sumMap添加String和FixedString键的支持【参考方案2】:

试试这个查询(您只需定义所需的“离散”列及其计数):

SELECT id, groupArray(result_per_id_column) result_per_id
FROM (
    SELECT id, groupArray(count_result) result_per_id_column
    FROM 
    (
        SELECT id, index_discrete.1 as index, (index_discrete.2,  count()) AS count_result
        FROM 
        (   
            SELECT id, arrayJoin(arrayMap((x, index) -> (index, x),
                            [discrete1, discrete2, discrete3 /* so on for other 'discrete'-columns */],
                            range(3 /* count of 'discrete'-columns */))) index_discrete
            FROM (
                /* test data */
                SELECT 
                    data.1 AS id, 
                    data.2 AS discrete1,
                    data.3 AS discrete2,
                    data.4 AS discrete3
                FROM 
                (
                    SELECT arrayJoin([(1, 'A', 'a', 'aa'), (1, 'B', 'b', 'aa'), (1, 'A', 'c', 'bb'), (2, 'C', 'a', 'bb'), (1, 'A', 'a', 'cc')]) AS data
                )))
        GROUP BY id, index_discrete.1, index_discrete.2
    )
    GROUP BY id, index
    ORDER BY id, index)
GROUP BY id
/* result
┌─id─┬─result_per_id──────────────────────────────────────────────────────────────┐
│  1 │ [[('A',3),('B',1)],[('a',2),('b',1),('c',1)],[('cc',1),('bb',1),('aa',2)]] │
│  2 │ [[('C',1)],[('a',1)],[('bb',1)]]                                           │
└────┴────────────────────────────────────────────────────────────────────────────┘
*/

【讨论】:

对不起,我改变了我的问题。 我修正了答案,考虑到对于一组“离散”列而不是许多列,它只返回一个具有数组类型的列。 谢谢。似乎对这个要求没有更多的“直接”支持。也许 clickhouse 可以有一些聚合函数,如 groupCountArray 等。【参考方案3】:
SELECT
    id,
    sumMap(arr, arrayResize(CAST([], 'Array(UInt64)'), length(arr), toUInt64(1))) AS s
FROM
(
    SELECT
        id,
        groupArrayArray([discrete1, discrete2]) AS arr
    FROM A
    GROUP BY id
)
GROUP BY id

┌─id─┬─s───────────────────────────────────┐
│  2 │ (['C','a'],[1,1])                   │
│  1 │ (['A','B','a','b','c'],[3,1,2,1,1]) │
└────┴─────────────────────────────────────┘


SELECT
    id,
    arrayMap((x, y) -> (x, y), (sumMap(arr, arrayResize(CAST([], 'Array(UInt64)'), length(arr), toUInt64(1))) AS s).1, s.2) AS r
FROM
(
    SELECT
        id,
        groupArrayArray([discrete1, discrete2]) AS arr
    FROM A
    GROUP BY id
)
GROUP BY id

┌─id─┬─r─────────────────────────────────────────┐
│  2 │ [('C',1),('a',1)]                         │
│  1 │ [('A',3),('B',1),('a',2),('b',1),('c',1)] │
└────┴───────────────────────────────────────────┘


SELECT
    id,
    arrayMap((x, y) -> (x, y),
      (arrayReduce('sumMap', [(groupArrayArray([discrete1, discrete2]) as arr)], 
              [arrayResize(CAST([], 'Array(UInt64)'), length(arr), toUInt64(1))]) as s).1,
      s.2) r
FROM A
GROUP BY id
┌─id─┬─r─────────────────────────────────────────┐
│  2 │ [('C',1),('a',1)]                         │
│  1 │ [('A',3),('B',1),('a',2),('b',1),('c',1)] │
└────┴───────────────────────────────────────────┘

【讨论】:

我不想将所有离散的列合并在一起,我想分别计算它们的百分比 @xiemeilong 我又添加了一个答案

以上是关于如何按不同的值将 clickhouse 中的 (value,count) 数组分组?的主要内容,如果未能解决你的问题,请参考以下文章

ClickHouse 单机安装及基础知识与 Spark 应用

熊猫,我怎样才能避免使用 iterrow (如何根据来自另一个数据帧的值将值分配给数据帧中的新列)

使用 ClickHouse 中另一个表中的值更新行

mysql - 如何使用父表中的值将连接表连接到另一个连接表?

如何按值将组作为 1 个带逗号的框?

如何告诉编译器可以安全地并行化循环?