Запрос занимает слишком много времени для запроса с предложением OR, но их части очень быстрые - PullRequest
0 голосов
/ 27 августа 2018

У меня есть две таблицы с ~ 1M строками, проиндексированными по их идентификаторам.

следующий запрос ...

SELECT t.* FROM transactions t
INNER JOIN integration it ON it.id_trans = t.id_trans
WHERE t.id_trans = '5440073'
OR it.id_integration = '439580587'

Этот запрос занимает около 30 секунд. Но ...

SELECT ... WHERE t.id_trans = '5440073'

занимает менее 100 мс и

SELECT ... WHERE it.id_integration = '439580587'

также занимает менее 100 мс. Даже

SELECT ... WHERE t.id_trans = '5440073'
UNION
SELECT ... WHERE it.id_integration = '439580587'

занимает менее 100 мс

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

Ответы [ 2 ]

0 голосов
/ 28 августа 2018

Почему OR такой медленный, а UNION такой быстрый?

Вы понимаете, почему UNION быстрый? Поскольку он может использовать два отдельных индекса с хорошим преимуществом и собирать несколько строк результатов из каждой части UNION, затем объединить результаты вместе.

Но почему OR не может этого сделать? Проще говоря, Оптимизатор не достаточно умен, чтобы попробовать этот угол.

В вашем случае тесты проводятся на разных таблицах; это приводит к радикально разным планам запросов (см. EXPLAIN SELECT ...) для двух частей UNION. Каждый может быть хорошо оптимизирован, поэтому каждый работает быстро.

Предполагая, что каждая часть доставляет только несколько строк, последующие издержки UNION незначительны, а именно - собрать два небольших набора строк, дедуплировать их (если вы используете UNION DISTINCT вместо UNION ALL), и доставить результаты.

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

(Еще один пример inflate-deflate - JOINs + GROUP BY. Обходные пути разные.)

0 голосов
/ 27 августа 2018

Я бы предложил написать запрос, используя UNION ALL:

SELECT t.*
FROM transactions t
WHERE t.id_trans = '5440073'
UNION ALL
SELECT t.*
FROM transactions t JOIN
     integration it 
     ON it.id_trans = t.id_trans
WHERE t.id_trans <> '5440073' AND
      it.id_integration = '439580587';

Примечание. Если идентификаторы действительно являются числами (а не строками), отбросьте одинарные кавычки. Смешанные типы могут иногда сбивать с толку оптимизатор и препятствовать использованию индексов.

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