You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ClickHouse分组查询仅对首次出现的唯一组合计数实现方案

ClickHouse 全局唯一组合首次出现计数实现方案

本问题是此前关于物化视图性能问题的延伸,核心需求为:统计指定维度组合的唯一值时,仅对全局首次出现的行计数,后续时间窗口重复出现的相同组合不再计数。

原有实现使用uniqState是按分组窗口独立去重,无法感知其他窗口是否已经出现过相同的(user_id, status, message_id)组合,因此会重复计数,可通过以下两种方案实现需求:


方案1:查询时实时计算(无需修改现有物化视图)

适合user_id维度下message_id量级不大、查询频率较低的场景,无需调整现有表结构:

小时粒度查询代码

WITH
    -- 先计算每个(user_id, status, message_id)的首次出现小时
    first_occur AS (
        SELECT
            user_id,
            multiIf(event_type_id IN (1,2), 10, 20) AS status,
            message_id,
            min(toStartOfHour(event_datetime)) AS first_hour
        FROM events
        WHERE user_id = 1
        GROUP BY user_id, status, message_id
    )
SELECT
    h.event_day_hour,
    h.status,
    h.total_count,
    -- 仅统计首次出现在当前小时的message_id数量
    count(f.message_id) AS unique_count
FROM (
    -- 原有小时聚合逻辑,先算每个窗口的总计数
    SELECT
        event_day_hour,
        multiIf(event_type_id IN (1,2), 10, 20) AS status,
        countMerge(count) AS total_count
    FROM events_hourly
    WHERE user_id = 1
    GROUP BY event_day_hour, status
) h
LEFT JOIN first_occur f ON h.status = f.status AND h.event_day_hour = f.first_hour
GROUP BY h.event_day_hour, h.status, h.total_count
ORDER BY h.event_day_hour, h.status;

天粒度查询代码

WITH
    -- 先计算每个(user_id, status, message_id)的首次出现天
    first_occur AS (
        SELECT
            user_id,
            multiIf(event_type_id IN (1,2), 10, 20) AS status,
            message_id,
            min(toYYYYMMDD(event_datetime)) AS first_day
        FROM events
        WHERE user_id = 1
        GROUP BY user_id, status, message_id
    )
SELECT
    h.day,
    h.status,
    h.total_count,
    count(f.message_id) AS unique_count
FROM (
    SELECT
        toYYYYMMDD(event_day_hour) AS day,
        multiIf(event_type_id IN (1,2), 10, 20) AS status,
        countMerge(count) AS total_count
    FROM events_hourly
    WHERE user_id = 1
    GROUP BY day, status
) h
LEFT JOIN first_occur f ON h.status = f.status AND h.day = f.first_day
GROUP BY h.day, h.status, h.total_count
ORDER BY h.day, h.status;

方案2:新增预聚合物化视图(高性能方案)

适合高频率查询场景,通过额外物化视图预存每个组合的首次出现时间,查询时无需扫描源表:

第一步:创建首次出现记录物化视图

CREATE MATERIALIZED VIEW events_first_occur
ENGINE = ReplacingMergeTree(event_datetime)
ORDER BY (user_id, status, message_id)
AS SELECT
    user_id,
    multiIf(event_type_id IN (1,2), 10, 20) AS status,
    message_id,
    min(event_datetime) AS event_datetime
FROM events
GROUP BY user_id, status, message_id;

使用ReplacingMergeTree自动保留每个组合最小的event_datetime,自动去重重复插入的相同组合

第二步:小时粒度查询代码

WITH
    first_occur AS (
        SELECT
            status,
            message_id,
            toStartOfHour(event_datetime) AS first_hour
        FROM events_first_occur
        WHERE user_id = 1
        GROUP BY status, message_id, first_hour
    )
SELECT
    h.event_day_hour,
    h.status,
    h.total_count,
    count(f.message_id) AS unique_count
FROM (
    SELECT
        event_day_hour,
        multiIf(event_type_id IN (1,2), 10, 20) AS status,
        countMerge(count) AS total_count
    FROM events_hourly
    WHERE user_id = 1
    GROUP BY event_day_hour, status
) h
LEFT JOIN first_occur f ON h.status = f.status AND h.event_day_hour = f.first_hour
GROUP BY h.event_day_hour, h.status, h.total_count
ORDER BY h.event_day_hour, h.status;

第三步:天粒度查询代码

WITH
    first_occur AS (
        SELECT
            status,
            message_id,
            toYYYYMMDD(event_datetime) AS first_day
        FROM events_first_occur
        WHERE user_id = 1
        GROUP BY status, message_id, first_day
    )
SELECT
    h.day,
    h.status,
    h.total_count,
    count(f.message_id) AS unique_count
FROM (
    SELECT
        toYYYYMMDD(event_day_hour) AS day,
        multiIf(event_type_id IN (1,2), 10, 20) AS status,
        countMerge(count) AS total_count
    FROM events_hourly
    WHERE user_id = 1
    GROUP BY day, status
) h
LEFT JOIN first_occur f ON h.status = f.status AND h.day = f.first_day
GROUP BY h.day, h.status, h.total_count
ORDER BY h.day, h.status;

内容的提问来源于stack exchange,提问作者ozahorulia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 04:06:02