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 = категорія). Чи є спосіб оптимізувати цей запит? Також зауважте, що у виробництві тимчасова таблиця #Ids не існує, це параметр, що оцінюється у таблиці, з тих же 10000 ідентифікаторів, переданих в процедуру збереження.
Додаткова інформація:
- індекси категорії на категорію_id та батьківська_категорія_id
- index_voc індекс категорії_id, код_код мови
- індекс категорії_посилання на sku_id, категорія_id
Редагувати (вирішено)
Як було зазначено у прийнятій відповіді, проблемою було положення АБО у категорії_посилання ПРИЄДНАЙТЕСЬ. Однак код, запропонований у прийнятій відповіді, дуже повільний, повільніший, ніж оригінальний код. Набагато швидше, а також чистіше рішення - просто замінити поточний стан 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.