Два запроса быстрее, чем один? - PullRequest
9 голосов
/ 26 сентября 2011

У меня есть таблица со столбцами:

CREATE TABLE aggregates (
    a VARHCAR,
    b VARCHAR,
    c VARCHAR,
    metric INT
    KEY test (a, b, c, metric)
);

Если я сделаю запрос вроде:

SELECT b, c, SUM(metric) metric
FROM aggregates
WHERE a IN ('a', 'couple', 'of', 'values')
GROUP BY b, c
ORDER BY b, c

Запрос занимает 10 секунд, объяснение:

+----+-------------+------------+-------+---------------+------+---------+------+--------+-----------------------------------------------------------+
| id | select_type | table      | type  | possible_keys | key  | key_len | ref  | rows   | Extra                                                     |
+----+-------------+------------+-------+---------------+------+---------+------+--------+-----------------------------------------------------------+
|  1 | SIMPLE      | aggregates | range | test          | test | 767     | NULL | 582383 | Using where; Using index; Using temporary; Using filesort |
+----+-------------+------------+-------+---------------+------+---------+------+--------+-----------------------------------------------------------+

Если я также сгруппирую / упорядочить по столбцу a, то мне не понадобится временная / файловая сортировка, но я сам сделаю то же самое в другом запросе:

SELECT b, c, SUM(metric) metric
FROM (
    SELECT a, b, c, SUM(metric) metric
    FROM aggregates
    WHERE a IN ('a', 'couple', 'of', 'values')
    GROUP BY a, b, c
    ORDER BY a, b, c
) t
GROUP BY b, c
ORDER BY b, c

Запрос занимает 1 секунду, а объяснение таково:

+----+-------------+------------+-------+---------------+------+---------+------+--------+---------------------------------+
| id | select_type | table      | type  | possible_keys | key  | key_len | ref  | rows   | Extra                           |
+----+-------------+------------+-------+---------------+------+---------+------+--------+---------------------------------+
|  1 | PRIMARY     | <derived2> | ALL   | NULL          | NULL | NULL    | NULL |    252 | Using temporary; Using filesort |
|  2 | DERIVED     | aggregates | range | test          | test | 767     | NULL | 582383 | Using where; Using index        |
+----+-------------+------------+-------+---------------+------+---------+------+--------+---------------------------------+

Почему это? Почему быстрее, если я делаю группировку в отдельном внешнем запросе, а не просто делаю все в одном?

Ответы [ 4 ]

2 голосов
/ 26 сентября 2011

Как работает SQL - чем меньше данных у вас на каждом шаге, тем быстрее будет выполняться запрос. Поскольку сначала вы выполняете группировку во внутреннем запросе, вы избавляетесь от большого количества данных, которые больше не нужно обрабатывать внешним запросом.

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

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

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

1 голос
/ 26 сентября 2011

В первом случае индекс используется для поиска совпадающих записей, но не может использоваться для сортировки, поскольку вы не включаете крайний левый столбец в предложения group / order by.Мне было бы интересно увидеть оба профиля запросов:

установить профилирование = 1;

выполнить запрос 1;

выполнить запрос 2;

показать профиль для запроса 1;

показать профиль для запроса 2;

0 голосов
/ 26 сентября 2011

Просто из любопытства, вы можете попробовать эту версию?:

SELECT b, c, SUM(metric) metric
FROM aggregates
WHERE a = 'some-value'
GROUP BY b, c

и этот:

SELECT b, c, metric
FROM (
    SELECT a, b, c, SUM(metric) metric
    FROM aggregates
    WHERE a = 'some-value'
    GROUP BY a, b, c
) t
ORDER BY b, c

и это:

SELECT b, c, SUM(metric) metric
FROM aggregates
WHERE a = 'some-value'
GROUP BY a, b, c
0 голосов
/ 26 сентября 2011

**** Отредактировано: не очень хороший ответ, так как я не видел часть where.

Я думаю, что просто во втором запросе MySQL использует индекс, а первый - нет. Если вы создадите индекс типа (b, c, metric), я почти уверен, что первый запрос будет быстрее второго.

Отредактировано, чтобы быть более многословным:

Первый запрос:

  • Недостаточно хорошего индекса для выполнения запроса.
  • Тестовый индекс включен (a, b, c, метрика), и вам нужен индекс для (b, c) ((b, c, метрика) также подойдет)
  • Возможно, MySQL использует тестовый индекс, но он не является хорошим индексом, так что это похоже на полное сканирование таблицы.

Второй запрос:

  • Использует индекс (a, b, c)
  • Во втором экземпляре выполняется неиндексный запрос, но с меньшим количеством данных, чем первый запрос.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...