You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

求助:按班次每10分钟计算重置式成本累计值的SQL查询优化

优化1600万条数据的班次10分钟成本累计查询方案

嘿,针对你这个处理1600万条数据、按班次每10分钟计算累计成本且班次结束重置的慢查询问题,我给你几个实战性的优化思路,亲测能大幅提升速度:

1. 把低效的关联子查询换成窗口函数

你当前的子查询是关联子查询,每一行数据都会触发一次子查询计算,1600万条数据相当于跑1600万次小查询,速度能快才怪!换成窗口函数只需要扫描表一次就能完成累计计算,效率提升N倍:

SELECT 
    shift,
    FLOOR(UNIX_TIMESTAMP(date)/600) AS window,
    -- 按班次分区,按时间排序,累计从当前班次开头到当前行的成本
    SUM(cost) OVER (PARTITION BY shift ORDER BY date) AS cumulative_cost
FROM expenses
-- 直接用时间范围过滤,比先计算window再过滤更友好
WHERE date > FROM_UNIXTIME(? * 600)
ORDER BY window DESC;

如果你的需求是每个10分钟窗口结束时的累计值,可以在外层加分组取每个窗口的最大累计值:

SELECT 
    shift,
    window,
    MAX(cumulative_cost) AS cumulative_cost
FROM (
    SELECT 
        shift,
        FLOOR(UNIX_TIMESTAMP(date)/600) AS window,
        SUM(cost) OVER (PARTITION BY shift ORDER BY date) AS cumulative_cost
    FROM expenses
    WHERE date > FROM_UNIXTIME(? * 600)
) t
GROUP BY shift, window
ORDER BY window DESC;

2. 给查询加覆盖索引,避免回表

1600万条数据的查询,索引是核心优化点。你需要创建一个覆盖索引,包含查询用到的所有字段,这样数据库不用回表查原数据,直接从索引里就能拿到结果:

-- MySQL 8.0+ 可以用INCLUDE指定非索引列,老版本直接把cost加入索引列
CREATE INDEX idx_expenses_shift_date_cost ON expenses(shift, date) INCLUDE (cost);
-- 老版本写法:
-- CREATE INDEX idx_expenses_shift_date_cost ON expenses(shift, date, cost);

这个索引完美匹配窗口函数的分区(shift)、排序(date)和求和字段(cost),能让查询速度飙升。

3. 避免在过滤条件里用函数,转成时间范围过滤

你原来的having window > ?里,window是通过函数计算出来的,数据库没法直接用索引过滤。把窗口参数转换成对应的时间范围,用date > 具体时间来过滤,就能让索引直接生效:
比如你传入的?是某个10分钟窗口的编号,对应的起始时间是FROM_UNIXTIME(? * 600),直接用这个时间作为过滤条件,比计算window后过滤高效太多。

4. 预计算汇总表(适合高频查询场景)

如果这个查询是业务里经常要跑的,不如直接建一个汇总表,定时预计算好每个班次每个10分钟窗口的累计值,查询的时候直接查汇总表,速度能到秒级:

首先创建汇总表:

CREATE TABLE expenses_summary (
    shift VARCHAR(50), -- 根据你实际的shift类型调整
    window BIGINT,
    cumulative_cost DECIMAL(18,2), -- 根据cost字段类型调整
    PRIMARY KEY (shift, window)
);

然后用定时任务(比如MySQL事件、Python脚本等)定期更新汇总表,只处理新增的数据:

REPLACE INTO expenses_summary(shift, window, cumulative_cost)
SELECT 
    shift,
    FLOOR(UNIX_TIMESTAMP(date)/600) AS window,
    MAX(SUM(cost) OVER (PARTITION BY shift ORDER BY date)) AS cumulative_cost
FROM expenses
WHERE date >= (SELECT MAX(date) FROM expenses_summary) -- 只更新上次汇总后新增的数据
GROUP BY shift, window;

之后查询直接查expenses_summary就行,完全不用碰1600万的原表。

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

火山引擎 最新活动