

新闻资讯
技术学院本文介绍如何通过合理设计 join 查询与针对性索引,高效统计“未被删除的用户”中、至少参与过一门“已激活且未被删除”的课程的人数,避免全表扫描与中间结果膨胀。
在处理 courses(约3000万行)、users(约3万行)和 participants(约3千行)三表关联统计时,核心目标是:快速获取满足以下全部条件的用户去重数量:
直觉上,先过滤再连接(filter-before-join)比先连接后过滤(join-then-filter)更高效。MySQL 的查询优化器虽能自动重排执行顺序,但其效果高度依赖索引支持。以下是经过结构化优化的完整方案:
SELECT COUNT(DISTINCT p.participant_id) FROM courses AS c INNER JOIN participants AS p ON c.id = p.course_id INNER JOIN users AS u ON p.participant_id = u.id WHERE u.deleted_at IS NULL AND c.active = 1 AND c.deleted_at IS NULL AND p.participant_type = 'Eloomi\\Models\\User';
该写法明确表达了业务逻辑(三表自然关联 + 精确过滤),同时为后续索引设计提供清晰依据。
| 表名 | 推荐索引 | 说明 |
|---|---|---|
| courses | INDEX(active, deleted_at) | 最优先创建。可快速定位满足 active=1 AND deleted_at IS NULL 的少量课程(即使总行数达3000万,符合条件的可能仅数千)。复合索引首列 active 支持等值筛选,第二列 deleted_at 支持 IS NULL 条件(MySQL 8.0+ 对 IS NULL 在二级索引中高效支持)。因 InnoDB 聚簇索引特性,该索引隐含包含主键 id,可直接用于 JOIN participants。 |
| participants | INDEX(course_id, participant_type, participant_id) | 按 course_id(外键)升序排列,确保能用上 courses 过滤后的 id 集合快速查找;加入 participant_type 实现覆盖过滤条件,避免回表;末尾 participant_id 用于后续关联 users 表及 COUNT(DISTINCT ...) 计算。 |
| users | INDEX(id, deleted_at) | 显式引导优化器使用该二级索引(而非默认主键聚簇索引)完成 p.participant_id = u.id AND u.deleted_at IS NULL 的联合查找。若发现执行计划仍走主键扫描,可添加索引提示:JOIN users AS u USE INDEX (id, deleted_a t)。 |
? 验证索引有效性:执行 EXPLAIN FORMAT=TREE(MySQL 8.0+)或 EXPLAIN 查看实际访问类型(应为 ref 或 range,避免 ALL/index 全扫);重点关注 key 列是否命中预期索引,rows 是否显著减少。
高效达成该统计的核心在于:以最小集合作为驱动表(courses),通过精准复合索引实现“先筛后连”,全程避免中间结果膨胀。三张表各一个针对性复合索引,配合简洁 JOIN 查询,即可在毫秒级响应大规模数据关联计数。务必通过 EXPLAIN 持续验证执行计划,并根据真实数据分布微调索引顺序。