未使用数据库索引导致查询缓慢
Posted
技术标签:
【中文标题】未使用数据库索引导致查询缓慢【英文标题】:Database index not being used causing slow query 【发布时间】:2014-06-05 12:08:11 【问题描述】:我有以下两张表:
StudentCourse
- Id,
- StudentId,
- CourseId
StudentId
和 CourseId
上的唯一索引
StudentCourseCount
- Id,
- Student1Id,
- Student2Id,
- CourseCount
Student1Id
和 CourseCount
的索引
Student2Id
和 CourseCount
上的索引
当我有CourseId
时,我会列出参加该课程的学生。我想要完成的关键是在一个学生下,我想列出他们以前一起学习过的其他学生。
我正在尝试以下查询:
SELECT * FROM StudentCourseCount sc
INNER JOIN StudentCourse s1 ON s1.course_id = <id> AND sc.student1_id = s1.student_id
INNER JOIN StudentCourse s2 ON s2.course_id = <id> AND sc.student2_id = s2.student_id
WHERE sc.course_count > 1
查询按预期工作;但是,在我的超大表(超过 10,000,000 行)上它非常慢。
当我解释查询时,StudentCourseCount
不使用索引。它正确识别出Student1Id
和Student2Id
可能存在索引,但两者都不使用。
执行计划:表:sc 可能的键:Student1Id,Student2Id 键: 空行:28648392
表:c2 键:student_id 行:1
表:c1 键:student_id 行:1
第一个表明显是在扫描,没有用key快速过滤。
【问题讨论】:
你能复制一下你的表的执行计划和索引定义吗? 我只是好奇。 . .哪个应用程序在StudentCourse
表中有 10,000,000 行?
我已经添加了索引并描述了查询执行计划(它没有很好地复制/粘贴,所以我重新编写了重要的部分)。
专业提示:避免在软件中使用SELECT *
,尤其是在访问大型表的查询中。返回所有列的需要会使优化器难以巧妙地满足您的查询。如果您重写查询以枚举所需的列,您将帮助我们推荐有用的索引策略。
如果它在 StudentCourseCount 上进行表扫描,我猜WHERE sc.course_count > 1
的选择性不够
【参考方案1】:
似乎您也应该将 course_id 过滤器放在外部选择中。 StudentCourseCount sc 上的唯一过滤器是 course_count。假设你只搜索 1 个 course_id,你应该有 sc.course_count>1 AND sc.course_id = id。否则,您的联接会尝试将过滤器应用于 sc.course_count>1 结果集。
假设值分布均匀,则此查询(或变体)应该是高效的。 10M 行不是很大,足够大,查询需要优化。
【讨论】:
sc 没有课程 ID,它包含学生及其人数。课程 ID 在 s1 和 s2 表中过滤。 啊,那么您似乎想先从 StudentCourse 表中进行选择,而不是 StudentCourseCount。【参考方案2】:我认为 Brent Baisley 有一个很好的观点,我一开始没有看到 <id>
。我猜你想让两个学生都在同一门课程中,这样你就可以在 Join 中链接他们并在 where 子句中获得 course_id=<id>
条件。我认为优化器应该自己做这些事情,但值得一试:
SELECT * FROM StudentCourseCount sc
INNER JOIN StudentCourse s1 ON sc.student1_id = s1.student_id
INNER JOIN StudentCourse s2 ON s2.course_id = s1.course_id AND sc.student2_id = s2.student_id
WHERE sc.course_count > 1 AND s1.course_id = <id>
【讨论】:
将 s1.course_id 移动到 where 子句中会更慢。所以这似乎没有帮助。 好的,那么问题又来了,您在 StudentCourseCount 和 StudentCourse 中有多少条记录?有多少 StudentCourseCount 记录符合 course_count>1 以及多少 StudentCourse 记录符合 course_id=这是一个非常大的查询,它返回的结果集非常大。由于返回的数据量很大,我不确定您是否可以对其进行优化。
SELECT *
FROM StudentCourseCount sc INNER JOIN
StudentCourse s1
ON s1.course_id = <id> AND sc.student1_id = s1.student_id INNER JOIN
StudentCourse s2
ON s2.course_id = <id> AND sc.student2_id = s2.student_id
WHERE sc.course_count > 1;
您想要的表索引是StudentCourseCount(course_count, student_id)
和StudentCourse(student_id, course_id)
。
现在,您说此查询有效,我假设您的意思是您喜欢结果。它正在回答以下问题:
获取所有上过课程
id
并且还上过不止一门课程的学生对
这与:
我想在一个学生下面列出他们以前一起学习过的其他学生。
如果这是您真正的问题,我建议您在 Stack Overflow 上提出另一个问题,以获得更好的查询。
【讨论】:
以上是关于未使用数据库索引导致查询缓慢的主要内容,如果未能解决你的问题,请参考以下文章