【问题标题】:concurrent count request optimization并发计数请求优化
【发布时间】:2018-08-01 07:57:07
【问题描述】:

我有一张表格,列出了建筑物中人员的所有签到和签出。 我的目标是计算在特定时间(比如每小时)有多少人在大楼里。

这是我的桌子:

CREATE TABLE checkins
(
    date_in DateTime, 
    date_out DateTime, 
    age Int32, 
    sex String, 
    date_day Date MATERIALIZED toDate(date_in)
) ENGINE = MergeTree(date_day, date_in, 8192)

示例数据

INSERT INTO checkins VALUES
    (toDateTime('2018-01-01 08:30:00'), toDateTime('2018-01-01 16:30:00'), 32, 'M'), 
    (toDateTime('2018-01-01 09:30:00'), toDateTime('2018-01-01 10:30:00'), 28, 'M'), 
    (toDateTime('2018-01-01 10:15:00'), toDateTime('2018-01-01 10:45:00'), 30, 'M'),
    (toDateTime('2018-01-01 11:30:00'), toDateTime('2018-01-01 11:45:00'), 35, 'M'), 
    (toDateTime('2018-01-01 14:30:00'), toDateTime('2018-01-01 17:30:00'), 25, 'F');

我目前正在以这种方式计算建筑物中的人数:

SELECT count(*), instant
FROM 
(
    SELECT arrayJoin([toDateTime('2018-01-01 10:00:00'), toDateTime('2018-01-01 12:00:00'), toDateTime('2018-01-01 14:00:00'), toDateTime('2018-01-01 16:00:00')] AS tabinstants) AS instant
    FROM checkins 
    WHERE (date_in < instant) AND (date_out > instant)
) 
GROUP BY instant
ORDER BY instant ASC

按预期返回

┌─count()─┬─────────────instant─┐
│       2 │ 2018-01-01 10:00:00 │
│       1 │ 2018-01-01 12:00:00 │
│       1 │ 2018-01-01 14:00:00 │
│       2 │ 2018-01-01 16:00:00 │
└─────────┴─────────────────────┘

但是,这个请求似乎不可扩展:数组中有很多点,表格中有很多行,这真的很慢。我认为这是因为连接数据的大小。有没有一种机制可以更有效地计算这些数据?

第二个问题: 如果我现在想在每个点之间设置最大人数,我该怎么办?

例如 在 10:00 到 12:00 之间,我最多有 3 人(在 10:15)

┌─count()─┬─────────────instant─┐
│       3 │ 2018-01-01 10:00:00 │ 
│       1 │ 2018-01-01 12:00:00 │
│       2 │ 2018-01-01 14:00:00 │
│       2 │ 2018-01-01 16:00:00 │
└─────────┴─────────────────────┘

【问题讨论】:

    标签: clickhouse


    【解决方案1】:

    您可以尝试生成 30 分钟周期数组的运算符 timeSlotsarrayFilter 过滤中间时间,因此您将有时间段。 所以你的查询将是这样的 SELECT count(*), instant FROM ( SELECT arrayJoin(arrayFilter(x -> toStartOfHour(x) = x, timeSlots(toDateTime('2018-01-01 08:00:00'), toUInt32(toDateTime('2018-01-01 20:00:00') - toDateTime('2018-01-01 08:00:00')))) AS tabinstants) AS instant FROM checkins WHERE (toStartOfHour(date_in) <= instant) AND (toStartOfHour(date_out) + 3600 > instant) ) GROUP BY instant ORDER BY instant ASC

    【讨论】:

    • 感谢您的回答。我对 arrayjoin 部分的问题是我希望能够减少到 5 分钟的周期。但是生成数组对我来说不是问题。关于你对第二个问题的提议,它不像我想要的那样工作。例如,如果我有一个人在 10:15 到 10:30 和另一个人在 10:45 到 11:00 之间,那么 10:00 到 11:00 的时段最多有一个人。
    • 关于第二个问题,要解决您描述的问题,您可以将条件更改为WHERE (toStartOfHour(date_in) &lt;= instant) AND (toStartOfHour(date_out - 1) + 3600&gt; instant)
    • 如果您需要 5 分钟的间隔。您可以使用 range(N) 生成 int 数组,然后使用 arrayMap 将其转换为 5 分钟间隔。 SELECT count(*), instant FROM ( SELECT arrayJoin(arrayMap(x -&gt; 300*x + toDateTime('2018-01-01 08:00:00'), range(toUInt32((toDateTime('2018-01-01 20:00:00') - toDateTime('2018-01-01 08:00:00'))/300))) AS tabinstants) AS instant FROM checkins WHERE (toStartOfFiveMinute(date_in) &lt;= instant) AND (toStartOfFiveMinute(date_out - 1) + 300 &gt; instant) ) GROUP BY instant ORDER BY instant ASC
    • 感谢您的持续回答。 arrayMap 的好技巧,我会用它!关于第二个问题,问题不是边界问题。它计算在插槽中的每个人,而不是同时出现的人。如果我修改我的示例,一个人从 10:15 到 10:20,另一个人从 10:30 到 10:35,两者都将计入 10:00 11:00 时段,但建筑物中最多有 1 个在给定的时间。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-11-22
    • 1970-01-01
    • 2015-08-02
    • 2021-09-05
    • 2019-02-16
    • 2016-03-18
    • 2016-04-29
    相关资源
    最近更新 更多