TL; DR
Поскольку этот вопрос продолжает получать взгляды, я кратко изложу его здесь, чтобы новичкам не пришлось страдать от истории:
JOIN table t ON t.member = @value1 OR t.member = @value2 -- this is slow as hell
JOIN table t ON t.member = COALESCE(@value1, @value2) -- this is blazing fast
-- Note that here if @value1 has a value, @value2 is NULL, and vice versa
Я понимаю, что это может быть проблемой не всех, но, подчеркнув чувствительность предложений ON, это может помочь вам смотреть в правильном направлении. В любом случае оригинальный текст здесь для будущих антропологов:
Оригинальный текст
Рассмотрим следующий простой запрос (задействованы только 3 таблицы)
SELECT
l.sku_id AS ProductId,
l.is_primary AS IsPrimary,
v1.category_name AS Category1,
v2.category_name AS Category2,
v3.category_name AS Category3,
v4.category_name AS Category4,
v5.category_name AS Category5
FROM category c4
JOIN category_voc v4 ON v4.category_id = c4.category_id and v4.language_code = 'en'
JOIN category c3 ON c3.category_id = c4.parent_category_id
JOIN category_voc v3 ON v3.category_id = c3.category_id and v3.language_code = 'en'
JOIN category c2 ON c2.category_id = c3.category_id
JOIN category_voc v2 ON v2.category_id = c2.category_id and v2.language_code = 'en'
JOIN category c1 ON c1.category_id = c2.parent_category_id
JOIN category_voc v1 ON v1.category_id = c1.category_id and v1.language_code = 'en'
LEFT OUTER JOIN category c5 ON c5.parent_category_id = c4.category_id
LEFT OUTER JOIN category_voc v5 ON v5.category_id = c5.category_id and v5.language_code = @lang
JOIN category_link l on l.sku_id IN (SELECT value FROM #Ids) AND
(
l.category_id = c4.category_id OR
l.category_id = c5.category_id
)
WHERE c4.[level] = 4 AND c4.version_id = 5
Это довольно простой запрос, единственная запутанная часть - последнее объединение категории, именно так, потому что категория 5 уровня может существовать или не существовать. В конце запроса я ищу информацию о категории для каждого идентификатора продукта (SKU ID), и именно здесь появляется очень большая таблица category_link. Наконец, таблица #Ids - это просто временная таблица, содержащая 10 000 идентификаторов.
При выполнении я получаю следующий фактический план выполнения:
Как вы можете видеть, почти 90% времени проводится во вложенных циклах (Inner Join). Вот дополнительная информация об этих вложенных циклах:
Обратите внимание, что имена таблиц не совпадают точно, потому что я отредактировал имена таблиц запросов для удобства чтения, но их довольно легко сопоставить (ads_alt_category = category). Есть ли способ оптимизировать этот запрос? Также обратите внимание, что в производственной среде временная таблица #Ids не существует, это параметр с табличным значением тех же 10 000 идентификаторов, которые передаются в хранимую процедуру.
Дополнительная информация:
- индексы категорий по category_id и parent_category_id
- указатель категории по номеру для идентификатора категории, код_языка
- указатель category_link по sku_id, category_id
Редактировать (решено)
Как указано в принятом ответе, проблема заключалась в предложении OR в соединении category_link JOIN. Однако код, предложенный в принятом ответе, очень медленный, даже медленнее, чем исходный код. Более быстрое, а также более чистое решение - просто заменить текущее условие JOIN следующим:
JOIN category_link l on l.sku_id IN (SELECT value FROM @p1) AND l.category_id = COALESCE(c5.category_id, c4.category_id)
Эта минутная настройка является самым быстрым решением, проверенным на соответствие двойному соединению из принятого ответа, а также проверенным на CROSS APPLY, как предложено valverij.