SQL Server查询优化:如何高效查询男女比例超70%的课程
优化后SQL(通用版本,兼容学生重复选课的场景)
SELECT CourseId, CAST(ROUND(CAST(Men AS FLOAT) / NULLIF(Women, 0) * 100, 2) AS NUMERIC(12,2)) AS Ratio FROM ( SELECT c.CourseId, COUNT(DISTINCT CASE WHEN s.Gender = 'M' THEN c.StudentId END) AS Men, COUNT(DISTINCT CASE WHEN s.Gender = 'F' THEN c.StudentId END) AS Women FROM Classrooms c INNER JOIN Students s ON c.StudentId = s.StudentId GROUP BY c.CourseId HAVING COUNT(DISTINCT CASE WHEN s.Gender = 'M' THEN c.StudentId END) > 0 AND COUNT(DISTINCT CASE WHEN s.Gender = 'F' THEN c.StudentId END) > 0 ) AS CourseGenderStat WHERE CAST(Men AS FLOAT) / Women * 100 > 70 ORDER BY CourseId
核心优化点
- 单次表扫描替代两次子查询:原写法需要两次扫描
Classrooms、Students表后再做关联,优化后仅需一次关联+一次分组计算,IO开销直接降低50%以上,数据量越大性能提升越明显。 - 异常兼容:通过
NULLIF和HAVING提前过滤无男生/无女生的课程,避免出现除以0的运行错误。 - 无重复计算:比例仅需计算一次,无需在SELECT和WHERE子句中重复写计算逻辑。
更高性能版本(确认选课无重复记录时使用)
如果Classrooms表是选课表,同一个学生不会重复选同一门课,可以去掉COUNT中的DISTINCT,用SUM替代,性能会进一步提升:
SELECT CourseId, CAST(ROUND(CAST(Men AS FLOAT) / Women * 100, 2) AS NUMERIC(12,2)) AS Ratio FROM ( SELECT c.CourseId, SUM(CASE WHEN s.Gender = 'M' THEN 1 ELSE 0 END) AS Men, SUM(CASE WHEN s.Gender = 'F' THEN 1 ELSE 0 END) AS Women FROM Classrooms c INNER JOIN Students s ON c.StudentId = s.StudentId GROUP BY c.CourseId HAVING SUM(CASE WHEN s.Gender = 'M' THEN 1 ELSE 0 END) > 0 AND SUM(CASE WHEN s.Gender = 'F' THEN 1 ELSE 0 END) > 0 ) AS CourseGenderStat WHERE CAST(Men AS FLOAT) / Women * 100 > 70 ORDER BY CourseId
索引优化建议
为了进一步提升查询性能,可以新增两个覆盖索引:
Students表新增联合索引(StudentId, Gender),关联时无需回表查询性别字段Classrooms表新增联合索引(CourseId, StudentId),分组和关联操作可以直接走索引完成,无需访问堆表或聚集索引
内容的提问来源于stack exchange,提问作者Sigal Cohen
相关产品推荐
相关产品推荐

