既然这个问题不断得到关注,我就在这里总结一下,让新人不用受历史的折磨:
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 不存在,它是传递给存储过程的相同 10‘000 个 ID 的表值参数。
附加信息:
编辑(已解决)
正如接受的答案所指出的,问题是 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 的建议针对 CROSS APPLY 进行了测试。
问题似乎出在这部分代码中:
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 )
or在加入条件总是可疑的。一个建议是将其拆分为两个连接:
or
JOIN category_link l1 on l1.sku_id in (SELECT value FROM #Ids) and l1.category_id = cr.category_id left outer join category_link l1 on l2.sku_id in (SELECT value FROM #Ids) and l2.category_id = cr.category_id
然后,您必须修改查询的其余部分来处理此问题。. . coalesce(l1.sku_id, l2.sku_id)例如在select子句中。
coalesce(l1.sku_id, l2.sku_id)
select