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

ClickHouse动态生成OHLC:如何排除查询结果中的最后一分钟

如何在ClickHouse生成OHLC时排除最后一分钟并避免重复聚合记录

问题场景

在生成分钟级OHLC(开盘/收盘/最高/最低)数据时,需要排除最后一分钟(未结束的分钟),避免通过物化视图动态生成时出现同一分钟多条未聚合的记录。原有实现中,插入新tick数据后,同分钟会产生多条重复的OHLC记录,无法自动合并。

原参考的分钟级OHLC生成逻辑:

SELECT 
  id,
  minute,
  max(value) AS high,
  min(value) AS low,
  avg(value) AS avg,
  argMin(value, timestamp) AS first,
  argMax(value, timestamp) AS last
FROM security
GROUP BY id, toStartOfMinute(timestamp) AS minute
ORDER BY minute

完整示例场景代码:

create table ticks_data
(
    symbol       String,
    datetime_msc DateTime64(3),
    price        Float64,
    volume       UInt64
)
engine = MergeTree PARTITION BY toYYYYMM(datetime_msc)
ORDER BY (symbol, datetime_msc);

INSERT INTO ticks_data (symbol, datetime_msc, price, volume)
VALUES ('EURUSD', '2022-06-03 18:01:51.265',1.07084,0);
INSERT INTO ticks_data (symbol, datetime_msc, price, volume)
VALUES ('EURUSD', '2022-06-03 18:01:51.027',1.071429,0);
INSERT INTO ticks_data (symbol, datetime_msc, price, volume)
VALUES ('EURUSD', '2022-06-03 18:01:51.948',1.07089,0);

CREATE TABLE charts
(
    symbol       String,
    datetime     DATETIME,
    high         Float64,
    low          Float64,
    vol          UInt64,
    open         Float64,
    close        Float64
)
ENGINE = AggregatingMergeTree
order by (symbol,datetime);

insert into table charts
SELECT
  symbol,
  datetime,
  max(price) AS high,
  min(price) AS low,
  sum(volume) AS vol,
  arrayElement(arraySort((x,y)->y,groupArray(price), groupArray(datetime_msc)), 1) AS open,
  arrayElement(arraySort((x,y)->y, groupArray(price), groupArray(datetime_msc)), -1) AS close
FROM ticks_data
GROUP BY symbol, toStartOfMinute(datetime_msc) AS datetime
ORDER BY datetime;

CREATE MATERIALIZED VIEW charts_MV to charts
AS
SELECT
  symbol,
  datetime,
  max(price) AS high,
  min(price) AS low,
  sum(volume) AS vol,
  arrayElement(arraySort((x,y)->y,groupArray(price), groupArray(datetime_msc)), 1) AS open,
  arrayElement(arraySort((x,y)->y, groupArray(price), groupArray(datetime_msc)), -1) AS close
FROM ticks_data
GROUP BY symbol, toStartOfMinute(datetime_msc) AS datetime
ORDER BY datetime;

INSERT INTO ticks_data (symbol, datetime_msc, price, volume)
VALUES ('EURUSD', '2022-06-03 18:01:54.265',1.07098,0)

解决方案

1. 修正表结构与物化视图(解决重复聚合问题)

原有实现直接插入聚合结果到AggregatingMergeTree,导致同分钟多次插入生成多条记录。正确做法是存储聚合状态而非直接结果,让ClickHouse自动合并状态:

-- 重新定义charts表,使用聚合函数State类型
DROP TABLE IF EXISTS charts;
CREATE TABLE charts
(
    symbol       String,
    datetime     DATETIME,
    high         AggregateFunction(max, Float64),
    low          AggregateFunction(min, Float64),
    vol          AggregateFunction(sum, UInt64),
    open         AggregateFunction(argMin, Float64, DateTime64(3)),
    close        AggregateFunction(argMax, Float64, DateTime64(3))
)
ENGINE = AggregatingMergeTree
ORDER BY (symbol, datetime);

-- 创建物化视图,插入聚合状态而非直接结果,同时过滤未结束的分钟
DROP MATERIALIZED VIEW IF EXISTS charts_MV;
CREATE MATERIALIZED VIEW charts_MV TO charts
AS
SELECT
  symbol,
  toStartOfMinute(datetime_msc) AS datetime,
  maxState(price) AS high,
  minState(price) AS low,
  sumState(volume) AS vol,
  argMinState(price, datetime_msc) AS open,
  argMaxState(price, datetime_msc) AS close
FROM ticks_data
-- 过滤当前未结束的分钟,只处理已完整的分钟
WHERE toStartOfMinute(datetime_msc) < toStartOfMinute(now())
GROUP BY symbol, datetime;

2. 查询最终OHLC数据

通过Merge函数将聚合状态计算为最终的OHLC值,如需动态排除最后一分钟,可添加过滤条件:

SELECT
  symbol,
  datetime,
  maxMerge(high) AS high,
  minMerge(low) AS low,
  sumMerge(vol) AS vol,
  argMinMerge(open) AS open,
  argMaxMerge(close) AS close
FROM charts
-- 可选:排除最后一分钟的记录
-- WHERE datetime != (SELECT max(datetime) FROM charts)
GROUP BY symbol, datetime
ORDER BY datetime;

3. 关键优化点

  • 用argMinState/argMaxState替代数组排序获取开盘/收盘价,大幅提升性能,避免大数据量下的内存占用问题。
  • 物化视图中直接过滤未结束的分钟,确保只处理已完成的分钟数据,无需后续手动排除。
  • AggregatingMergeTree会自动合并相同(symbol, datetime)的聚合状态,最终每条分钟数据仅保留一条聚合记录,避免重复。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 03:54:56