由于性能低下,如何优化 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 约束强制执行。不需要查询studentstable,只需要查询值为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 进行聚合。但这可能是一件好事,因为按标题聚合会合并两个碰巧同名的不同课程;这可能不是你想要的。