MySQL按分组获取最小/最大日期的查询调优及可读性优化咨询
MySQL按分组获取最小/最大日期的查询调优及可读性优化咨询
首先得说,你的原始查询能得到正确结果已经很棒了,但多层嵌套的结构确实让逻辑绕了弯路,可读性大打折扣,而且可能没充分利用你已有的索引,导致12秒的执行时间还有优化空间。咱们先从简化逻辑、提升可读性开始,再聊性能优化的点。
一、先简化查询逻辑,让代码更易懂
你的核心需求是按col2分组,拿到每个分组my_date的最小和最大值,而且你提到每个col2分组内的日期范围本身就不会超过7天。那原始查询里基于最小/最大id的日期过滤,如果不是业务上必须要限定某个7天窗口的话,其实是多余的——直接分组聚合就能得到结果:
SELECT col2, MIN(my_date) AS min_date, MAX(my_date) AS max_date FROM `table` WHERE col4 = 1 -- 看你原始查询里多次限定col4=1,应该是业务需要过滤这个字段? GROUP BY col2;
如果你的业务逻辑确实需要基于col4=1的最小id日期加7天、最大id日期减7天来过滤数据,那咱们可以用CTE(公共表表达式)把基准日期的逻辑单独抽出来,避免多次关联同一张表,让逻辑更清晰:
-- 先一次性拿到需要的日期边界 WITH date_bounds AS ( SELECT DATE_ADD((SELECT my_date FROM `table` WHERE col4=1 ORDER BY id LIMIT 1), INTERVAL 7 DAY) AS upper_min_bound, DATE_SUB((SELECT my_date FROM `table` WHERE col4=1 ORDER BY id DESC LIMIT 1), INTERVAL 7 DAY) AS lower_max_bound ) SELECT t.col2, MIN(t.my_date) AS min_date, MAX(t.my_date) AS max_date FROM `table` t CROSS JOIN date_bounds WHERE t.col4 = 1 AND t.my_date < date_bounds.upper_min_bound AND t.my_date > date_bounds.lower_max_bound GROUP BY t.col2;
用CTE把日期边界的逻辑单独拎出来,整个查询的结构一目了然,后续维护也方便很多。
二、性能优化的关键:用好索引
你的表有几百万行,索引是提升性能的核心,给你两个具体的建议:
- 创建贴合查询的覆盖索引
你已经有(col2, col3, col4, my_date)的复合唯一键,但这个索引的顺序不太贴合你的查询逻辑——你是先过滤col4=1,再按col2分组,最后聚合my_date。建议创建一个专门的覆盖索引:
CREATE INDEX idx_col4_col2_mydate ON `table` (col4, col2, my_date);
这个索引的顺序是先过滤col4,再按col2分组,最后直接取my_date做聚合——属于覆盖索引,查询时不需要回表到主数据,直接从索引里就能拿到所有需要的数据,执行速度会大幅提升。
减少不必要的表扫描
原始查询里多次关联同一张表,相当于多次扫描全表(或者大范围内的数据),用CTE一次性获取日期边界,再做一次交叉连接,能减少表扫描的次数,降低数据库的计算负载。确认过滤条件的必要性
如果业务规则里的“每个col2分组的min/max日期在7天内”只是指分组后的结果符合这个要求,而不是要过滤掉超出7天的数据,那直接用最简化的分组查询就行,不需要额外的日期过滤,这样性能会更好。
三、验证优化效果
你可以用EXPLAIN命令查看查询的执行计划,对比原始查询和优化后查询的差异:
EXPLAIN SELECT -- 把你的原始查询或优化后的查询放在这里
如果优化后的查询执行计划里出现Using index的标记,说明成功用到了覆盖索引,性能肯定会有明显提升。
备注:内容来源于stack exchange,提问作者Wannabe-Coder

