Lui*_*rao 43 performance sql-server
TL; 博士
由于这个问题一直有意见,我在这里总结一下,这样新人就不必忍受历史:
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
Run Code Online (Sandbox Code Playgroud)
我意识到这可能不是每个人的问题,但通过突出 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
Run Code Online (Sandbox Code Playgroud)
这是一个非常简单的查询,唯一令人困惑的部分是最后一个类别连接,这是因为类别级别 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)
Run Code Online (Sandbox Code Playgroud)
这个微小的调整是最快的解决方案,针对已接受答案的双重连接进行了测试,并根据valverij 的建议针对 CROSS APPLY 进行了测试。
Gor*_*off 19
问题似乎出在这部分代码中:
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
)
Run Code Online (Sandbox Code Playgroud)
orin 加入条件总是可疑的。一个建议是将其拆分为两个连接:
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
Run Code Online (Sandbox Code Playgroud)
然后您必须修改查询的其余部分来处理这个问题。. . coalesce(l1.sku_id, l2.sku_id)例如在select条款中。
正如另一位用户所提到的,此加入可能是原因:
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
)
Run Code Online (Sandbox Code Playgroud)
除了将这些拆分为多个连接之外,您还可以尝试 CROSS APPLY
CROSS APPLY (
SELECT [some column(s)]
FROM category_link x
WHERE EXISTS(SELECT value FROM #Ids WHERE value = x.sku_id)
AND (x.category_id = c4.category_id OR x.category_id = c5.category_id)
) l
Run Code Online (Sandbox Code Playgroud)
从上面的 MSDN 链接:
表值函数作为右输入,外表表达式作为左输入。为左输入中的每一行评估右输入,并将生成的行组合起来作为最终输出。
基本上,APPLY就像一个子查询,它首先过滤掉右边的记录,然后将它们应用于查询的其余部分。
这篇文章很好地解释了它是什么以及何时使用它:http : //explainextended.com/2009/07/16/inner-join-vs-cross-apply/
然而,重要的是要注意,CROSS APPLY并不总是比INNER JOIN. 在许多情况下,它可能大致相同。但是,在极少数情况下,我实际上看到它变慢了(同样,这完全取决于您的表结构和查询本身)。
作为一般经验法则,如果我发现自己加入了一个包含太多条件语句的表格,那么我倾向于倾向于 APPLY
还有一个有趣的提示:OUTER APPLY会像一个LEFT JOIN
另外,请注意我选择使用EXISTS而不是IN. 在执行IN子查询时,请记住它会返回整个结果集,即使它找到了您的值。EXISTS但是,使用,它将在找到匹配项的瞬间停止子查询。
| 归档时间: |
|
| 查看次数: |
97787 次 |
| 最近记录: |