Скорость ключевого слова IN в MySQL / PostgreSQL - PullRequest
8 голосов
/ 05 июня 2009

Я слышал, как многие люди говорили, что ключевое слово IN в большинстве реляционных баз данных медленное. Насколько это правда? Пример запроса был бы таким, вне моей головы:

SELECT * FROM someTable WHERE someColumn IN (value1, value2, value3)

Я слышал, что это намного медленнее, чем это:

SELECT * FROM someTable WHERE
  someColumn = value1 OR
  someColumn = value2 OR
  someColumn = value3

Это правда? Или разница в скорости незначительна? Если это имеет значение, я использую PostgreSQL, но я также хотел бы узнать, как работает MySQL (и если он отличается) Заранее спасибо.

Ответы [ 7 ]

13 голосов
/ 05 июня 2009

В PostgreSQL именно то, что вы здесь получите, зависит от базовой таблицы, поэтому вы должны использовать EXPLAIN ANALYZE в некоторых примерах запросов к полезному подмножеству ваших данных, чтобы точно определить, что собирается делать оптимизатор (убедитесь, что таблицы, с которыми вы работаете, тоже были проанализированы). IN может быть обработан несколькими различными способами, и поэтому вам нужно посмотреть на некоторые примеры, чтобы выяснить, какая альтернатива используется для ваших данных. На ваш вопрос нет простого общего ответа.

Что касается конкретного вопроса, который вы добавили в свою ревизию, для тривиального набора данных без индексов, вот пример двух планов запросов, которые вы получите:

postgres=# explain analyze select * from x where s in ('123','456');
 Seq Scan on x  (cost=0.00..84994.69 rows=263271 width=181) (actual time=0.015..1819.702 rows=247823 loops=1)
   Filter: (s = ANY ('{123,456}'::bpchar[]))
 Total runtime: 1931.370 ms

postgres=# explain analyze select * from x where s='123' or s='456';
 Seq Scan on x  (cost=0.00..90163.62 rows=263271 width=181) (actual time=0.014..1835.944 rows=247823 loops=1)
   Filter: ((s = '123'::bpchar) OR (s = '456'::bpchar))
 Total runtime: 1949.478 ms

Эти две среды выполнения по существу идентичны, потому что в реальном времени обработки преобладает последовательное сканирование по таблице; многократный запуск показывает разницу между ними ниже допустимого диапазона погрешности. Как видите, PostgreSQL преобразует регистр IN в свой ЛЮБОЙ фильтр, который всегда должен выполняться быстрее, чем серия OR. Опять же, этот тривиальный случай не обязательно отражает то, что вы увидите в серьезном запросе, где задействованы индексы и тому подобное. В любом случае, ручная замена IN на серию операторов OR никогда не должна выполняться быстрее, потому что оптимизатор знает, что лучше всего сделать здесь, если у него есть хорошие данные для работы.

В общем, PostgreSQL знает больше хитростей для оптимизации сложных запросов, чем оптимизатор MySQL, но он также сильно зависит от того, что вы дали оптимизатору достаточно данных для работы. Первые ссылки в разделе «Оптимизация производительности» вики-сайта PostgreSQL охватывают самые важные вещи, необходимые для получения хороших результатов от оптимизатора.

8 голосов
/ 05 июня 2009

В MySQL это полные синонимы для оптимизатора:

SELECT  *
FROM    someTable
WHERE   someColumn IN (value1, value2, value3)

и

SELECT  *
FROM    someTable
WHERE   someColumn = value1 OR
        someColumn = value2 OR
        someColumn = value3

, при условии, что value являются литеральными константами или предустановленными переменными.

Согласно документации :

Определение условия диапазона для индекса из одной части выглядит следующим образом:

  • Для индексов BTREE и HASH сравнение ключевой части с постоянным значением является условием диапазона при использовании =, <=>, IN(), IS NULL или IS NOT NULL операторы.
  • Для всех типов индексов условия множественного диапазона в сочетании с OR или AND образуют условие диапазона.

«Постоянное значение» в предыдущих описаниях означает одно из следующего:

  • Константа из строки запроса
  • Столбец const или системной таблицы из того же соединения
  • Результат некоррелированного подзапроса
  • Любое выражение, состоящее полностью из подвыражений предыдущих типов

Однако этот запрос:

SELECT  *
FROM    table
WHERE   id = 1
        OR id = (SELECT id FROM other_table WHERE unique_condition)

будет использовать индекс на id, а этот:

SELECT  *
FROM    table
WHERE   id IN (1, (SELECT id FROM other_table WHERE unique_condition))

будет использовать fullscan.

I. е. есть разница, когда один из value является подзапросом из одной строки.

Я недавно подал это как ошибка 45145 в MySQL (оказалось, что 5.2 специфично, отсутствует в 5.1 и исправлено в 6.0 )

5 голосов
/ 05 июня 2009

Использование IN не обязательно медленное, это то, как вы строите параметры IN, которые значительно замедляют процесс. Слишком часто люди используют SELECT ... WHERE x IN (SELECT ..., который может быть очень плохо оптимизирован (т. Е. Совсем нет). Выполните поиск по «коррелированному подзапросу», чтобы увидеть, насколько плохим он может быть.

Зачастую вам вообще не нужно использовать IN, вместо этого вы можете использовать JOIN и использовать преимущества производных таблиц.

SELECT * FROM table1 WHERE x IN (SELECT y FROM table2 WHERE z=3)

Можно перефразировать следующим образом

SELECT * FROM table1 JOIN (SELECT y FROM table2 WHERE z=3) AS table2 ON table1.x=table2.y

Если синтаксис IN медленный, синтаксис JOIN часто будет намного быстрее. Вы можете использовать EXPLAIN, чтобы увидеть, как каждый запрос будет оптимизирован по-разному. Это упрощенный пример, и ваша база данных может показывать один и тот же путь запроса, но более сложные запросы обычно показывают что-то другое.

1 голос
/ 06 июня 2009

Я думаю, что вы получили ответ (ы), который вы хотели выше. Просто хотел добавить одну вещь.

Вам необходимо оптимизировать IN и использовать его правильно. В процессе разработки я всегда устанавливал раздел отладки внизу страницы каждый раз, когда появляется запрос, и он автоматически запускает EXPLAIN EXTENDED для каждого SELECT, а затем SHOW WARNINGS, чтобы увидеть (вероятный) способ, которым MySQL Query Optimizer переписывает запрос внутренне. Из этого можно многому научиться, как убедиться, что IN работает на вас.

1 голос
/ 05 июня 2009

IN с подвыбором часто медленный. IN со списком значений не должен быть медленнее, чем someColumn = value1 ИЛИ someColumn = value2 ИЛИ someColumn = value3 и т. Д. Это достаточно быстро, если число значений является нормальным.

IN с подзапросом медленный, когда оптимизатор не может найти хороший способ выполнить запрос, и должен использовать очевидный метод построения полного результата подзапроса. Например:

SELECT username
  FROM users
  WHERE userid IN (
    SELECT userid FROM users WHERE user_first_name = 'Bob'
  )

будет намного медленнее, чем

SELECT username FROM users WHERE user_first_name = 'Bob'

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

0 голосов
/ 05 июня 2009

Скорость ключевого слова IN будет зависеть от сложности вашего подзапроса. В приведенном вами примере вы просто хотите увидеть, находится ли значение someColumns в заданном списке значений, причем довольно короткое. Так что я думаю, что в этом случае стоимость производительности будет очень минимальной.

0 голосов
/ 05 июня 2009

В документации сказано, что IN очень быстро работает в MySQL, но в данный момент я не могу найти источник.

...