由于性能低下,如何优化 SQL 查询?
How to Optimise SQL Query due to Slow Performance?
由于性能低下,我正在寻求有关优化以下查询的帮助。 tables 和记录在我的机器上不存在(帮助朋友)因此我没有插入记录,我所知道的是 tables 很大并且包含数百万行数据.我已收到创建语句以显示 table 与外键等的关系
查询内容如下:
SELECT
tutor.tutor_id,
tutor.subject_id,
course.title,
SUBSTR(score.date, 4, 2) AS month,
AVG(score.score)
FROM
tutor
LEFT JOIN course ON tutor.course_id = course.course_id
LEFT JOIN score ON tutor.course_id = score.course_id
WHERE
course.course_id IS NOT NULL
AND score.date > '01-MAR-2020'
AND score.student_id in (SELECT student_id FROM students)
GROUP BY
tutor.tutor_id,
tutor.subject_id,
course.title,
SUBSTR(score.date, 4, 2)
;
看完上面的查询后,我了解到 where 子句中有一个子 select 查询可能会减慢查询速度,这将是转换为 INNER JOIN 的第一步。
我正在考虑我还能做些什么来加快查询速度,例如 selecting 字段(它已经有了)。唯一的另一件事是添加一个 LIMIT 来示例查询结果(不能 运行 这来测试我自己)并且还添加一个 EXPLAIN PLAN 来了解是什么导致结果返回如此缓慢的延迟。另一个选项是检查连接中的列是否已编入索引。
创建语句
CREATE TABLE Score (
score_id integer,
student_id integer,
course_id integer,
date date,
score integer,
PRIMARY KEY(score_id),
FOREIGN KEY(student_id) REFERENCES Students(student_id),
FOREIGN KEY(course_id) REFERENCES Course(course_id)
);
CREATE TABLE Student (
student_id integer,
first_name varchar,
last_name varchar,
group_id integer,
PRIMARY KEY(student_id),
FOREIGN KEY(group_id) REFERENCES Groups(group_id)
);
CREATE TABLE Groups (
group_id integer,
name varchar,
PRIMARY KEY(group_id)
);
CREATE TABLE Course (
course_id integer,
title varchar,
PRIMARY KEY(course_id)
);
CREATE TABLE Tutor (
course_id integer,
tutor_id integer,
group_id integer,
PRIMARY KEY(tutor_id),
FOREIGN KEY(course_id) REFERENCES Course(course_id),
FOREIGN KEY(group_id) REFERENCES Groups(group_id)
);
如果有人能帮助我理解,如果我遗漏了什么,或者我在尝试优化我的查询时是否沿着正确的方向前进,我将不胜感激。
- 您的查询未使用
LEFT OUTER JOIN
,因为在 WHERE
过滤器中,score.date > '01-MAR-2020'
和 course.course_id IS NOT NULL
永远无法匹配 NULL
值,因此 OUTER
加入的行将被淘汰。
- 鉴于
WHERE course.course_id IS NOT NULL
是多余的,因为连接子句 ON tutor.course_id = course.course_id
也强制执行 NOT NULL
条件。
'01-MAR-2020'
不是日期;它是一个看起来像日期的字符串文字,依赖于 SQL 引擎从 string-to-date 执行隐式转换。请改用 DATE '2020-03-01'
。
score.student_id in (SELECT student_id FROM students)
由 FOREIGN KEY
约束强制执行。不需要查询students
table,只需要查询值为NOT NULL
.
SUBSTR(score.date, 4, 2)
依赖于隐式 date-to-string 转换。使用 EXTRACT(MONTH FROM score.date)
直接获取月份编号(如果你想将 2022-03 与 2020-03 分组)或使用 TRUNC(score.date, 'MM')
(如果 2020-03 不应与 2021-03 分组)。
这使得您的查询:
SELECT tutor.tutor_id,
tutor.subject_id,
course.title,
TRUNC(score.date, 'MM') AS month,
AVG(score.score)
FROM tutor
INNER JOIN course ON tutor.course_id = course.course_id
INNER JOIN score ON tutor.course_id = score.course_id
WHERE score.date > DATE '2020-03-01'
AND score.student_id IS NOT NULL
GROUP BY
tutor.tutor_id,
tutor.subject_id,
course.title,
TRUNC(score.date, 'MM');
至于优化:
您可以在加入其他 table 之前尝试汇总分数:
SELECT t.tutor_id,
t.subject_id,
c.title,
s.month,
s.avg_score
FROM ( SELECT course_id,
TRUNC(date, 'MM') AS month,
AVG(score) AS avg_score
FROM score
WHERE date > DATE '2020-03-01'
AND student_id IS NOT NULL
GROUP BY
course_id,
TRUNC(date, 'MM')
) s
INNER JOIN tutor t ON t.course_id = s.course_id
INNER JOIN course c ON c.course_id = s.course_id
这意味着您通过主键 course_id
而不是 course.title
进行聚合。但这可能是一件好事,因为按标题聚合会合并两个碰巧同名的不同课程;这可能不是你想要的。
由于性能低下,我正在寻求有关优化以下查询的帮助。 tables 和记录在我的机器上不存在(帮助朋友)因此我没有插入记录,我所知道的是 tables 很大并且包含数百万行数据.我已收到创建语句以显示 table 与外键等的关系
查询内容如下:
SELECT
tutor.tutor_id,
tutor.subject_id,
course.title,
SUBSTR(score.date, 4, 2) AS month,
AVG(score.score)
FROM
tutor
LEFT JOIN course ON tutor.course_id = course.course_id
LEFT JOIN score ON tutor.course_id = score.course_id
WHERE
course.course_id IS NOT NULL
AND score.date > '01-MAR-2020'
AND score.student_id in (SELECT student_id FROM students)
GROUP BY
tutor.tutor_id,
tutor.subject_id,
course.title,
SUBSTR(score.date, 4, 2)
;
看完上面的查询后,我了解到 where 子句中有一个子 select 查询可能会减慢查询速度,这将是转换为 INNER JOIN 的第一步。
我正在考虑我还能做些什么来加快查询速度,例如 selecting 字段(它已经有了)。唯一的另一件事是添加一个 LIMIT 来示例查询结果(不能 运行 这来测试我自己)并且还添加一个 EXPLAIN PLAN 来了解是什么导致结果返回如此缓慢的延迟。另一个选项是检查连接中的列是否已编入索引。
创建语句
CREATE TABLE Score (
score_id integer,
student_id integer,
course_id integer,
date date,
score integer,
PRIMARY KEY(score_id),
FOREIGN KEY(student_id) REFERENCES Students(student_id),
FOREIGN KEY(course_id) REFERENCES Course(course_id)
);
CREATE TABLE Student (
student_id integer,
first_name varchar,
last_name varchar,
group_id integer,
PRIMARY KEY(student_id),
FOREIGN KEY(group_id) REFERENCES Groups(group_id)
);
CREATE TABLE Groups (
group_id integer,
name varchar,
PRIMARY KEY(group_id)
);
CREATE TABLE Course (
course_id integer,
title varchar,
PRIMARY KEY(course_id)
);
CREATE TABLE Tutor (
course_id integer,
tutor_id integer,
group_id integer,
PRIMARY KEY(tutor_id),
FOREIGN KEY(course_id) REFERENCES Course(course_id),
FOREIGN KEY(group_id) REFERENCES Groups(group_id)
);
如果有人能帮助我理解,如果我遗漏了什么,或者我在尝试优化我的查询时是否沿着正确的方向前进,我将不胜感激。
- 您的查询未使用
LEFT OUTER JOIN
,因为在WHERE
过滤器中,score.date > '01-MAR-2020'
和course.course_id IS NOT NULL
永远无法匹配NULL
值,因此OUTER
加入的行将被淘汰。 - 鉴于
WHERE course.course_id IS NOT NULL
是多余的,因为连接子句ON tutor.course_id = course.course_id
也强制执行NOT NULL
条件。 '01-MAR-2020'
不是日期;它是一个看起来像日期的字符串文字,依赖于 SQL 引擎从 string-to-date 执行隐式转换。请改用DATE '2020-03-01'
。score.student_id in (SELECT student_id FROM students)
由FOREIGN KEY
约束强制执行。不需要查询students
table,只需要查询值为NOT NULL
.SUBSTR(score.date, 4, 2)
依赖于隐式 date-to-string 转换。使用EXTRACT(MONTH FROM score.date)
直接获取月份编号(如果你想将 2022-03 与 2020-03 分组)或使用TRUNC(score.date, 'MM')
(如果 2020-03 不应与 2021-03 分组)。
这使得您的查询:
SELECT tutor.tutor_id,
tutor.subject_id,
course.title,
TRUNC(score.date, 'MM') AS month,
AVG(score.score)
FROM tutor
INNER JOIN course ON tutor.course_id = course.course_id
INNER JOIN score ON tutor.course_id = score.course_id
WHERE score.date > DATE '2020-03-01'
AND score.student_id IS NOT NULL
GROUP BY
tutor.tutor_id,
tutor.subject_id,
course.title,
TRUNC(score.date, 'MM');
至于优化:
您可以在加入其他 table 之前尝试汇总分数:
SELECT t.tutor_id,
t.subject_id,
c.title,
s.month,
s.avg_score
FROM ( SELECT course_id,
TRUNC(date, 'MM') AS month,
AVG(score) AS avg_score
FROM score
WHERE date > DATE '2020-03-01'
AND student_id IS NOT NULL
GROUP BY
course_id,
TRUNC(date, 'MM')
) s
INNER JOIN tutor t ON t.course_id = s.course_id
INNER JOIN course c ON c.course_id = s.course_id
这意味着您通过主键 course_id
而不是 course.title
进行聚合。但这可能是一件好事,因为按标题聚合会合并两个碰巧同名的不同课程;这可能不是你想要的。