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可能存在或可能不存在。在查询结束时,我正在寻找每个产品ID(SKU ID)的类别信息,这就是超大表category_link出现的地方。最后,表#Ids只是一个包含10'000 ID的临时表。
执行后,我得到以下实际执行计划:
如您所见,几乎90%的时间都用在嵌套循环(内部联接)中。以下是有关这些嵌套循环的更多信息:
请注意,表名并不完全匹配,因为我编辑了查询表名以提高可读性,但是非常容易匹配(ads_alt_category = category)。有什么方法可以优化此查询?还要注意,在生产中,临时表#Ids不存在,它是传递给存储过程的相同10000个ID的表值参数。
附加信息:
- category_id和parent_category_id上的类别索引
- category_id,language_code上的category_voc索引
- sku_id,category_id上的category_link索引
编辑(已解决)
正如公认的答案所指出的那样,问题出在category_link JOIN中的OR子句。但是,接受的答案中建议的代码非常慢,甚至比原始代码还慢。更快,更清洁的解决方案是简单地用以下命令替换当前的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)
这一分钟的调整是最快的解决方案,针对公认答案中的两次联接进行了测试,也针对Valverij建议的“交叉应用”进行了测试。