Использование WHERE с COALESCE чрезвычайно медленно - PullRequest
0 голосов
/ 27 марта 2019

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

SELECT * FROM main_i i JOIN main_p p ON p.item_id=i.id
WHERE COALESCE(p.provider_title_id, i.provider_title_id) = "X"

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

SELECT * FROM main_i i JOIN main_p p ON p.item_id=i.id
WHERE p.provider_title_id = "X"
UNION
SELECT * FROM main_i i JOIN main_p p ON p.item_id=i.id
WHERE i.provider_title_id = "X" AND p.provider_title_id IS NULL

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

Что мне здесь использовать?

Ответы [ 3 ]

1 голос
/ 27 марта 2019

В вашем первом запросе предикат COALESCE(p.provider_title_id, i.provider_title_id) = "X" основан на столбцах из двух таблиц.Это заставляет MySQL использовать этот предикат в качестве «предиката фильтрации», а не «предиката доступа».

Что это означает на английском языке?Это означает, что MySQL выполняет перекрестный продукт, полученный в результате JOIN, используя предикаты доступа [надеюсь, быстрый], но затем он вынужден фильтровать весь набор результатов, используя условие выше.Перекрестное произведение, вероятно, приводит к огромному количеству строк, которые будут отброшены условием.Много работы для небольшого результата.

Второй запрос использует простые предикаты, которые MySQL может использовать для доступа к строкам.На этот раз доступны только несколько строк.Предикат фильтрует некоторые из них, но общий объем работы очень ограничен.

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

1 голос
/ 27 марта 2019

По моему опыту, использование функций (таких как COALESCE) останавливает двигатели дб, чтобы оптимизировать выбор строки заранее.Механизмы БД не уверены в конечном результате, пока не выполнят каждую запись, полученную в результате объединения таблиц с помощью таких функций.Напротив, с вашим вторым запросом движки БД точно знают, что фильтровать только при создании начального набора (до того, где применяется).Возможно, вы можете выбрать это в плане объяснения запроса.

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

SELECT *
FROM main_i i JOIN
     main_p p
     ON p.item_id = i.id
WHERE p.provider_title_id = 'X'
OR
(p.provider_title_id IS NULL 
AND i.provider_title_id = 'X')
0 голосов
/ 27 марта 2019

Это нормально, но вы должны использовать UNION ALL:

SELECT *
FROM main_i i JOIN
     main_p p
     ON p.item_id = i.id
WHERE p.provider_title_id = 'X'
UNION ALL
SELECT *
FROM main_i i JOIN
     main_p p 
     ON p.item_id = i.id
WHERE i.provider_title_id = 'X' AND p.provider_title_id IS NULL;

Эта версия позволяет оптимизатору видеть два более простых подзапроса.Каждый подзапрос может использовать различные наборы индексов.В целом, SQL плохо справляется с неравенствами и OR в JOIN условиях.

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