求助:按班次每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




