MySQL关联无关表执行聚合查询超时原因咨询
为什么你的MySQL查询会超时?问题拆解与优化方案
让我来帮你分析下这个查询超时的核心原因,其实是几个性能杀手叠加在一起导致的:
1. 隐式笛卡尔积直接撑爆中间结果集
你写的FROM salaries a, employees b属于隐式交叉连接,在没有指定表间直接关联条件(比如a.emp_no = b.emp_no)的情况下,MySQL会先把两个表的所有行做笛卡尔积。举个例子,如果salaries有100万条薪资记录,employees有10万员工,那中间临时结果集就是1000亿行——这量级完全超出了数据库的处理能力,自然会超时。
2. 函数过滤导致索引失效
你用month(b.hire_date)和month(a.to_date)来匹配月份,这种对字段直接做函数操作的写法,会让原本可能存在的hire_date或to_date索引无法被使用。数据库不得不对两个表做全表扫描,再去计算每个日期的月份,进一步拖慢了查询速度。
3. 不必要的distinct增加计算开销
如果employees表的emp_no是主键(这是很常见的设计),那每个emp_no本身就是唯一的,count(distinct b.emp_no)完全等价于count(b.emp_no),额外的distinct只会让数据库多做一层去重计算,浪费资源。
优化后的查询方案
我们可以先分别对两个表按月份做聚合,再关联结果,这样中间结果集会小得多:
SELECT e.employees, e.hire_month, s.to_month FROM -- 先统计每个入职月份的员工数 (SELECT COUNT(emp_no) AS employees, MONTH(hire_date) AS hire_month FROM employees GROUP BY MONTH(hire_date)) e -- 关联薪资表的结束月份分组 JOIN (SELECT MONTH(to_date) AS to_month FROM salaries GROUP BY MONTH(to_date)) s ON e.hire_month = s.to_month;
如果你的需求是统计每个入职月份+薪资结束月份组合的员工数(和原查询的分组逻辑一致),可以调整为:
SELECT COUNT(DISTINCT e.emp_no) AS employees, e.hire_month, s.to_month FROM (SELECT emp_no, MONTH(hire_date) AS hire_month FROM employees) e JOIN (SELECT MONTH(to_date) AS to_month FROM salaries) s ON e.hire_month = s.to_month GROUP BY e.hire_month, s.to_month;
进一步提升性能的索引建议
为了让查询更快,你可以给日期字段添加基于月份的生成列并建索引:
-- 给employees表添加生成列和索引 ALTER TABLE employees ADD COLUMN hire_month TINYINT GENERATED ALWAYS AS (MONTH(hire_date)) STORED; ALTER TABLE employees ADD INDEX idx_hire_month (hire_month); -- 给salaries表添加生成列和索引 ALTER TABLE salaries ADD COLUMN to_month TINYINT GENERATED ALWAYS AS (MONTH(to_date)) STORED; ALTER TABLE salaries ADD INDEX idx_to_month (to_month);
之后查询就可以直接用生成列hire_month和to_month,不用再计算,索引也能被高效使用。
内容的提问来源于stack exchange,提问作者ee8291
相关产品推荐
相关产品推荐

