Как я могу улучшить эффективность этого SQL-запроса, который охватывает 4 таблицы и имеет несколько вложенных отчетов - PullRequest
1 голос
/ 28 декабря 2010
SELECT MIN(tL.id) AS classLessonID,
       tC.class AS className,
       CONCAT((SELECT term
                 FROM hl_classes_term
                WHERE active = '1'),
              LPAD((SELECT COUNT(*) as num
                      FROM hl_classes_lessons
                     WHERE class_id = tC.id
                       AND id <= MIN(tL.id)), 2, '0')) AS classLessonNum
  FROM hl_classes_lessons tL, hl_classes tC
 WHERE tL.id NOT IN (SELECT lesson_id
                       FROM hl_classes_answers
                      WHERE student = '".USER_ID."')
   AND tL.term_id = (SELECT id
                       FROM hl_classes_term
                      WHERE active = '1')
   AND tL.class_id = tC.id
 GROUP BY tL.class_id

Просто глядя на это, я знаю, что заставил тебя съежиться. Как я могу сделать этот запрос SQL более эффективным. Сейчас он работает быстро, но это потому, что в моей таблице не так много строк. Я думаю, что для ответа на этот вопрос вам нужно знать больше о моей базе данных.

Я использую MySQL. У меня есть четыре стола:

  • hl_classes (int id, int Professor, класс varchar, текстовое описание)
  • hl_classes_lessons (int id, int class_id, int term_id, varchar lessonTitle, varchar lexiconLink, текстовый урокData)
  • hl_classes_answers (int id, int lesson_id, int student, текст submit_answer, int процент)
  • hl_classes_terms (int id, datetime createtime, метка varchar, int term, int active)

hl_classes хранит все классы на сайте.

Уроки являются индивидуальными уроками для каждого класса. Класс может иметь бесконечные уроки. Каждый урок доступен в определенный срок.

hl_classes_terms хранит список всех терминов, а текущий термин имеет поле active = '1'.

Когда пользователь отправляет свои ответы на урок, он сохраняется в hl_classes_answers. Пользователь может ответить на каждый урок только один раз. На уроки нужно отвечать последовательно.

Возвращаясь к моему запросу, он должен сделать следующее: Выберите следующий урок, на который пользователь (USER_ID) должен ответить для каждого класса в текущем семестре. Выберите только следующий урок, получите уникальный идентификатор урока, имя класса, а затем специальный номер, который мы используем, чтобы показать пользователям «урок». Число представляет собой комбинацию термина + (слева добавлено 0) урок n. То есть, первый семестр, первый урок - 101. 4-й семестр, 12-й урок - 412.

В настоящее время этот запрос работает, но я хотел бы знать, как заставить его работать более эффективно.

Ответы [ 2 ]

2 голосов
/ 28 декабря 2010

Я вижу две вещи, которые могут существенно повлиять на эффективность запросов:

  1. Подзапрос в SELECT - этот подзапрос запускается для каждой строки в вашем результатезадавать.Это может составить довольно много.Лучше создать отдельный подзапрос, который можно присоединить к вашему основному запросу в виде таблицы:
    SELECT ...
    FROM hl_classes_lessons tL
    JOIN hl_classes tC ON tL.class_id = tC.id
    JOIN (
    SELECT id, class_id COUNT(*) AS num
    FROM hl_classes_lessons
    ) AS awesome_join ON awesome_join.class_id = tC.id AND awesome_join.id [less than or equal to] MIN(tL.id)
    WHERE ...
  1. Плохое предложение FROM - избегайте использования SELECT ..С синтаксисом table1, table2, table3.Вместо.сделать что-то вроде

<code>    FROM hl_classes_lessons tL
    JOIN hl_classes tC ON tL.class_id = tC.id
    WHERE   tL.id NOT IN (  SELECT lesson_id
               FROM hl_classes_answers
               WHERE student = ".USER_ID." )
    AND tL.term_id = ( SELECT id
               FROM hl_classes_term
               WHERE active = 1 )
После исправления этих ошибок я бы убедился, что соответствующие столбцы имеют индексы (hl_classes_lessons.class_id, hl_classes.id).Запустите ваш запрос с EXPLAIN , чтобы увидеть, есть ли индекс, который должен использоваться, но не по какой-то причине.

И, наконец, я бы избегал включения целых чиселв одинарных кавычках .

1 голос
/ 28 декабря 2010

Я создал набор таблиц и некоторые образцы данных, как показано ниже.

2-е издание

С дополнительной информацией о том, что каждый ученик должен брать каждый класс, затем запрос нижеработает правильно для студентов без ответов или с некоторыми ответами.Он не определяет, когда ученик отправил ответы на все уроки в данном классе.

SELECT AL.Class_ID,
       C.Class,
       100 * T.Term + AL.NextLessonNum AS ClassLessonNum
  FROM (SELECT L.Term_ID, L.Class_ID, COUNT(A.Lesson_ID) + 1 AS NextLessonNum
          FROM hl_classes_lessons AS L
          LEFT JOIN hl_classes_answers AS A
            ON A.Lesson_ID = L.ID AND A.Student = 221
         GROUP BY L.Term_ID, L.Class_ID
       ) AS AL
  JOIN hl_classes       AS C ON C.ID = AL.Class_ID
  JOIN hl_classes_terms AS T ON T.ID = AL.Term_ID  AND T.Active = 1;

Следующие ответы должны быть отправлены учеником 221:

724    History    102
722    Maths 1    101
723    English    102
721    Physics    103

Запрос ученику 220:

SELECT AL.Class_ID,
       C.Class,
       100 * T.Term + AL.NextLessonNum AS ClassLessonNum
  FROM (SELECT L.Term_ID, L.Class_ID, COUNT(A.Lesson_ID) + 1 AS NextLessonNum
          FROM hl_classes_lessons AS L
          LEFT JOIN hl_classes_answers AS A
            ON A.Lesson_ID = L.ID AND A.Student = 220
         GROUP BY L.Term_ID, L.Class_ID
       ) AS AL
  JOIN hl_classes       AS C ON C.ID = AL.Class_ID
  JOIN hl_classes_terms AS T ON T.ID = AL.Term_ID  AND T.Active = 1;

Ученик 220 еще не ответил:

724    History    101
722    Maths 1    101
723    English    101
721    Physics    101

1-е издание

Я думаю, что этот запрос дает правильные результаты для любого учащегося -В приведенном здесь примере используется студент 221:

SELECT AL.Class_ID,
       C.Class,
       100 * T.Term + AL.NextLessonNum AS ClassLessonNum
  FROM (SELECT L.Term_ID, L.Class_ID, COUNT(*) + 1 AS NextLessonNum
          FROM hl_classes_answers AS A
          JOIN hl_classes_lessons AS L ON A.Lesson_ID = L.ID AND A.Student = 221
         GROUP BY L.Term_ID, L.Class_ID
       ) AS AL
  JOIN hl_classes       AS C ON C.ID = AL.Class_ID
  JOIN hl_classes_terms AS T ON T.ID = AL.Term_ID  AND T.Active = 1;

. Для приведенных ниже данных результат:

724    History    102
723    English    102
721    Physics    103

Мне кажется правильным.Общая версия запроса (не ограничена одним студентом) выглядит следующим образом:

SELECT AL.Student,
       AL.Class_ID,
       C.Class,
       100 * T.Term + AL.NextLessonNum AS ClassLessonNum
  FROM (SELECT A.Student, L.Term_ID, L.Class_ID, COUNT(*) + 1 AS NextLessonNum
          FROM hl_classes_answers AS A
          JOIN hl_classes_lessons AS L ON A.Lesson_ID = L.ID
         GROUP BY A.Student, L.Term_ID, L.Class_ID
       ) AS AL
  JOIN hl_classes       AS C ON C.ID = AL.Class_ID
  JOIN hl_classes_terms AS T ON T.ID = AL.Term_ID  AND T.Active = 1
 ORDER BY AL.Student, AL.Class_ID;

Это дает результат:

221    721    Physics    103
221    723    English    102
221    724    History    102
224    721    Physics    102
224    722    Maths 1    102
224    723    English    102
224    724    History    102
227    724    History    103
228    722    Maths 1    102
228    723    English    102
231    721    Physics    102
231    723    English    102
232    722    Maths 1    102
232    724    History    102
233    721    Physics    102
233    723    English    102

Как отмечается в комментарии к основному вопросу, яповерьте, что в вашей схеме БД есть проблема типа «курица и яйцо»: вы не знаете, что учащийся посещает определенный класс, пока он не отправит успешный ответ - поэтому вы не можете сказать им, на какие классы он должен уметьвведите ответы, прежде чем они получат записанный ответ.После того, как они отправили свой первый ответ для определенного класса, вы можете продолжить, но как они идентифицированы, как принимающие класс?

Следовательно, я не пытался иметь дело с вводом ответа для класса 101 - который должен быть добавлен в БД другим способом.

Я также не пытался иметь делов ситуации, когда ученик представил ответы на все уроки в определенном классе.


Таблицы

hl_classes_terms

create table hl_classes_terms
(
    id         int primary key,
    createtime timestamp, -- datetime year to second,
    label      varchar(15),
    term       int,
    active     int
);

hl_classes

create table hl_classes
(
    id          int primary key,
    professor   int,
    class       varchar(25),
    description varchar(50)
);

hl_classes_lessons

create table hl_classes_lessons
(
     id          int primary key,
     class_id    int,
     term_id     int,
     lessonTitle varchar(25),
     lexiconLink varchar(25),
     lessonData varchar(25)
);

hl_classes_answers

create table hl_classes_answers
(
    id            int primary key,
    lesson_id     int,
    student       int,
    submit_answer varchar(255),
    percent       int
);

Данные

hl_classes_terms

CURRENT - это способ использования СУБД (IBM Informix Dynamic Server)) записывает текущую метку времени.

insert into hl_classes_terms values(345, current, "michaelmas", 1, 1);
insert into hl_classes_terms values(346, current, "lent",       2, 0);
insert into hl_classes_terms values(347, current, "summer",     3, 0);
insert into hl_classes_terms values(348, current, "stray",      4, 0);

hl_classes

insert into hl_classes values(721, 1, "Physics", "Physics");
insert into hl_classes values(722, 5, "Maths 1", "Maths 1");
insert into hl_classes values(723, 2, "English", "English");
insert into hl_classes values(724, 5, "History", "History");

hl_classes_lessons

insert into hl_classes_lessons values(913, 721, 345, "Physics L1", "link", "data");
insert into hl_classes_lessons values(914, 724, 345, "History L1", "link", "data");
insert into hl_classes_lessons values(915, 722, 345, "Maths   L1", "link", "data");
insert into hl_classes_lessons values(916, 723, 345, "English L1", "link", "data");
insert into hl_classes_lessons values(917, 721, 345, "Physics L2", "link", "data");
insert into hl_classes_lessons values(918, 724, 345, "History L2", "link", "data");
insert into hl_classes_lessons values(919, 721, 345, "Physics L3", "link", "data");

hl_classes_answers

insert into hl_classes_answers values(1211, 913, 221, "Answer", 20);
insert into hl_classes_answers values(1212, 917, 221, "Answer", 30);
insert into hl_classes_answers values(1213, 914, 221, "Answer", 40);
insert into hl_classes_answers values(1214, 916, 221, "Answer", 50);
insert into hl_classes_answers values(1215, 913, 224, "Answer", 60);
insert into hl_classes_answers values(1216, 914, 224, "Answer", 70);
insert into hl_classes_answers values(1217, 915, 224, "Answer", 80);
insert into hl_classes_answers values(1218, 916, 224, "Answer", 90);
insert into hl_classes_answers values(1219, 914, 227, "Answer", 50);
insert into hl_classes_answers values(1220, 918, 227, "Answer", 60);
insert into hl_classes_answers values(1221, 915, 228, "Answer", 70);
insert into hl_classes_answers values(1222, 916, 228, "Answer", 80);
insert into hl_classes_answers values(1223, 916, 231, "Answer", 90);
insert into hl_classes_answers values(1224, 913, 231, "Answer", 80);
insert into hl_classes_answers values(1225, 914, 232, "Answer", 70);
insert into hl_classes_answers values(1226, 915, 232, "Answer", 60);
insert into hl_classes_answers values(1227, 916, 233, "Answer", 50);
insert into hl_classes_answers values(1228, 913, 233, "Answer", 40);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...