Получать данные с помощью одного и быстрого SQL-запроса - PullRequest
1 голос
/ 19 января 2009

У меня есть следующие данные:

ExamEntry   Student_ID     Grade
  11           1             80
  12           2             70
  13           3             20
  14           3             68
  15           4             75

Я хочу найти всех студентов, которые сдали экзамен. В этом случае, если экзаменов мало один ученик, которому нужно было заняться, мне нужно найти последний результат.

Итак, в этом случае я получу, что все студенты сдали.

Могу ли я найти его одним быстрым запросом? Я делаю это так:

  1. Найти список записей по выберите max (ExamEntry) из группы данных по Student_ID

  2. Найти результаты:

выберите ExamEntry из данных, где ExamEntry в ().

Но это ОЧЕНЬ медленно - я получаю около 1000 записей, и этот двухэтапный процесс занимает 10 секунд.

Есть ли лучший способ?

Спасибо.

Ответы [ 8 ]

6 голосов
/ 19 января 2009

Если ваш запрос очень медленный с 1000 записями в вашей таблице, значит что-то не так. Для современной системы базы данных таблица, содержащая 1000 записей, считается очень очень маленькой.
Скорее всего, вы не указали (первичный) ключ для своей таблицы?

Если предположить, что учащийся сдает экзамен, если хотя бы одна оценка выше необходимого минимума, соответствующий запрос будет:

SELECT 
  Student_ID
, MAX(Grade) AS maxGrade
FROM table_name
GROUP BY Student_ID
HAVING maxGrade > MINIMUM_GRADE_NEEDED

Если вам действительно нужно, чтобы последняя оценка была выше минимальной:

SELECT 
  Student_ID
, Grade
FROM table_name
WHERE ExamEntry IN ( 
    SELECT 
      MAX(ExamEntry) 
    FROM table_name 
    GROUP BY Student_ID
)
HAVING Grade > MINIMUM_GRADE_NEEDED
1 голос
/ 25 января 2009

Я использовал подсказки, приведенные здесь, и здесь я обнаружил, что запрос выполняется почти на 3 порядка быстрее, чем мой первый (.03 сек вместо 10 сек):

SELECT ExamEntry, Student_ID, Grade from data,
       ( SELECT max(ExamEntry) as ExId GROUP BY Student_ID) as newdata
WHERE `data`.`ExamEntry`=`newdata`.`ExId` AND Grade > 60;

Спасибо всем!

1 голос
/ 19 января 2009

Как уже упоминалось, индексирование является мощным инструментом для ускорения запросов. Порядок индекса, однако, принципиально важен.

Индекс в порядке (ExamEntry) затем (Student_ID) затем (Grade) будет почти бесполезен для поиска экзаменов, где студент сдал.

Индекс в обратном порядке подошел бы идеально, если бы все, что вам нужно, - это найти, какие экзамены были сданы. Это позволило бы обработчику запросов быстро идентифицировать строки для сданных экзаменов и просто обрабатывать их.

В MS SQL Server это можно сделать с помощью ...

CREATE INDEX [IX_results] ON [dbo].[results] 
(
    [Grade],
    [Student_ID],
    [ExamEntry]
)
ON [PRIMARY]

(я рекомендую прочитать больше об индексах, чтобы увидеть, какие есть другие опции, такие как ClusterdIndexes и т. Д., И т. Д.)

С этим индексом следующий запрос сможет очень быстро игнорировать «провальные» экзамены и просто отображать студентов, которые когда-либо сдавали экзамен ...

(Предполагается, что если вам когда-нибудь исполнится более 60 лет, вы будете считаться пропуском, даже если впоследствии вы снова сдадите экзамен и получите 27).

SELECT
    Student_ID
FROM
    [results]
WHERE
    Grade >= 60
GROUP BY
    Student_ID

Если вам определенно нужно самое последнее значение, вам нужно изменить порядок индекса обратно на что-то вроде ...

CREATE INDEX [IX_results] ON [dbo].[results] 
(
    [Student_ID],
    [ExamEntry],
    [Grade]
)
ON [PRIMARY]

Это потому, что первое, что нас интересует, - это самый последний экзамен для каждого учащегося. Что может быть достигнуто с помощью следующего запроса ...

SELECT
   *
FROM
   [results]
WHERE
   [results].ExamEntry = (
                          SELECT
                              MAX([student_results].ExamEntry)
                          FROM
                              [results] AS [student_results]
                          WHERE
                              [student_results].Student_ID = [results].student_id
                         )
   AND [results].Grade > 60

Подобный подзапрос может показаться медленным, тем более что он выполняется для каждой строки в [results].

Это, однако, не тот случай ...
- Основной и дополнительный запросы ссылаются на одну и ту же таблицу
- Механизм запросов просматривает индекс для каждого уникального Student_ID
- Подзапрос выполнен, для этого Student_ID
- Механизм запросов уже находится в этой части индекса
- Таким образом, новый поиск по индексу не требуется

EDIT:

Был сделан комментарий, что при 1000 записях индексы не актуальны. Следует отметить, что в вопросе указано, что возвращено 1000 записей, а не то, что таблица содержит 1000 записей. Если бы базовый запрос занимал столько времени, сколько указано, я бы поспорил, что в таблице много более 1000 записей. Может быть, это можно уточнить?

EDIT:

Я только что исследовал 3 запроса с 999 записями в каждом (3 результата экзамена для каждого из 333 студентов)

Метод 1: ГДЕ a.ExamEntry = (ВЫБЕРИТЕ МАКС. (B.ExamEntry) ИЗ РЕЗУЛЬТАТОВ [a] ГДЕ a.Student_ID = b.student_id)

Метод 2: ГДЕ a.ExamEntry IN (ВЫБЕРИТЕ МАКС. (ExamEntry) ИЗ РЕЗУЛЬТАТОВ GROUP BY Student_ID)

Метод 3: ИСПОЛЬЗОВАНИЕ ВНУТРЕННЕГО СОЕДИНЕНИЯ вместо предложения IN

Были найдены следующие моменты:

Method    QueryCost(No Index)   QueryCost(WithIndex)
   1               23%                    9%
   2               38%                   46%
   3               38%                   46%

Итак, Query 1 быстрее независимо от индексов, но индексы также определенно делают способ 1 существенно быстрее.

Причина этого в том, что индексы разрешают поиск, где в противном случае вам нужно сканирование. Разница между линейным и квадратным законами.

1 голос
/ 19 января 2009

Я сделаю некоторые предположения, что у вас есть таблица ученика и тестовая таблица, а таблица, которую вы нам показываете, является таблицей test_result ... (если у вас нет похожей структуры, вам следует пересмотреть свою схему)

select s.id, s.name, t.name, max(r.score)
from student s
left outer join test_result r on r.student_id = s.id
left outer join test t on r.test_id = t.id
group by s.id, s.name, t.name

Все поля с идентификатором должны быть проиндексированы.

Если в вашем домене действительно есть только один тест (тип) ... тогда запрос будет

select s.id, s.name, max(r.score)
from student s
left outer join test_result r on r.student_id = s.id
group by s.id, s.name
1 голос
/ 19 января 2009
SELECT student_id, MAX(ExamEntry)
FROM data
WHERE Grade > :threshold
GROUP BY student_id

Как это?

0 голосов
/ 28 января 2009

не используйте

where grade > 60

но

where grade between 60 and 100

это должно идти быстрее

0 голосов
/ 25 января 2009
select examentry,student_id,grade 
from data 
where examentry in 
  (select max(examentry) 
   from data 
   where grade > 60 
   group by student_id)
0 голосов
/ 20 января 2009

Спасибо за ответы !!

Я думаю, что Демс, вероятно, ближе всего к тому, что мне нужно, но я немного подробнее остановлюсь на этом вопросе.

  1. Учитываются только последние оценки. Если ученик скончался в первый раз, пришел снова и потерпел неудачу, он потерпел полное поражение. Он / она мог бы принять участие в 3 или 4 экзаменах, но учитывается только последний.
  2. Я использую сервер MySQL. Проблема, с которой я сталкиваюсь как в Linux, так и в Windows.
  3. Мой набор данных сейчас составляет около 2 тыс. Записей и растет со скоростью ~ 1 тыс. За новый экзамен.
  4. Запрос на конкретный экзамен также возвращает ~ 1K записей, когда ~ 1K будет числом посещенных студентов (полученных SELECT DISTINCT STUDENT_ID из результатов;), тогда почти все пройдены, а некоторые не пройдены. *

  5. Я выполняю следующий запрос в моем коде: ВЫБЕРИТЕ ExamEntry, Student_ID из экзаменов, ГДЕ ExamEntry в (SELECT MAX (ExamEntry) из экзаменов GROUP BY Student_ID). Поскольку подзапрос возвращает около ~ 1 тыс. Записей, кажется, что основной запрос просматривает их в цикле, в результате чего весь запрос выполняется в течение очень долгого времени и при 50% загрузке сервера (100% в Windows).

  6. Я чувствую , что есть лучший способ :-), просто пока не могу его найти.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...