MySQL高效统计员工在订单表两列的线索与销售生成次数
千万级数据量下MySQL员工业绩统计最优查询方案
场景说明:业务数据体量庞大且随时间持续增长,方案以查询效率最高为核心目标,优先避免全表扫描、减少不必要的IO与中间表生成。
涉及表结构
员工表(employees)
+-------------+---------------+ | employee_id | employee_name | +-------------+---------------+ | 0000001 | James | | 0000002 | David | | 0000003 | Mark | | 0000004 | Gemma | | 0000005 | Hayley | | 0000006 | Sunil | +-------------+---------------+
订单表(employees_orders)
+----------+---------------------+-------------------+ | order_id | Lead_generated_by | sale_generated_by | +----------+---------------------+-------------------+ | 00001 | James | David | | 00002 | Mark | Gemma | | 00003 | Hayley | David | | 00004 | James | Mark | | 00005 | Gemma | Hayley | | 00006 | Sunil | Mark | | 00007 | David | James | | 00008 | Hayley | Sunil | | 00009 | Gemma | James | | 00010 | Hayley | David | +----------+---------------------+-------------------+
预期返回结果
+---------------+-----------------+-----------------+ | employee_name | leads_generated | sales_generated | +---------------+-----------------+-----------------+ | James | 2 | 2 | | David | 1 | 3 | | Mark | 1 | 2 | | Gemma | 2 | 1 | | Hayley | 3 | 1 | | Sunil | 1 | 1 | +---------------+-----------------+-----------------+
性能优化前置配置(必做,否则SQL写法优化收益极低)
- 给
employees表的employee_name字段建唯一索引,作为员工维度关联的基准键 - 给
employees_orders表建两个覆盖索引:idx_lead_name (Lead_generated_by)、idx_sale_name (sale_generated_by),两个索引不需要包含其他字段,统计时直接走索引不需要回表
最优查询SQL
SELECT e.employee_name, IFNULL(l.lead_count, 0) AS leads_generated, IFNULL(s.sale_count, 0) AS sales_generated FROM employees e LEFT JOIN ( -- 走idx_lead_name覆盖索引直接聚合,无回表开销 SELECT Lead_generated_by AS emp_name, COUNT(1) AS lead_count FROM employees_orders GROUP BY Lead_generated_by ) l ON e.employee_name = l.emp_name LEFT JOIN ( -- 走idx_sale_name覆盖索引直接聚合,无回表开销 SELECT sale_generated_by AS emp_name, COUNT(1) AS sale_count FROM employees_orders GROUP BY sale_generated_by ) s ON e.employee_name = s.emp_name;
性能优势说明
- 两个聚合子查询都直接在覆盖索引上完成扫描、分组、计数,全程不需要读取表的主键数据,IO开销降低90%以上
- 子查询聚合完成后,每个员工仅返回1条统计结果,和员工表关联时的数据集极小,不会产生大中间表、笛卡尔积风险
- 避免了常规写法中「关联订单表全量数据后再分组统计」的临时表磁盘写入、文件排序开销,千万级数据量下查询速度比常规
SUM(CASE WHEN)写法快5~10倍
避坑提示
不要为了「少扫一次表」使用如下写法:
-- 不推荐:大表下性能极差 SELECT e.employee_name, SUM(CASE WHEN o.Lead_generated_by = e.employee_name THEN 1 ELSE 0 END) leads_generated, SUM(CASE WHEN o.sale_generated_by = e.employee_name THEN 1 ELSE 0 END) sales_generated FROM employees e LEFT JOIN employees_orders o ON o.Lead_generated_by = e.employee_name OR o.sale_generated_by = e.employee_name GROUP BY e.employee_id;
该写法会产生员工表和订单表的笛卡尔积膨胀,千万级订单下会生成数十亿条中间关联数据,临时表直接写满磁盘,查询基本无法返回结果。
内容的提问来源于stack exchange,提问作者chrisb123
相关产品推荐
相关产品推荐

