And*_*rew 11 sql-server execution-plan sql-server-2017
我正在尝试生成一个示例查询计划来说明为什么联合两个结果集比在 JOIN 子句中使用 OR 更好。我写的一个查询计划让我很难过。我正在使用 StackOverflow 数据库和 Users.Reputation 上的非聚集索引。
CREATE NONCLUSTERED INDEX IX_NC_REPUTATION ON dbo.USERS(Reputation)
SELECT DISTINCT Users.Id
FROM dbo.Users
INNER JOIN dbo.Posts
ON Users.Id = Posts.OwnerUserId
OR Users.Id = Posts.LastEditorUserId
WHERE Users.Reputation = 5
Run Code Online (Sandbox Code Playgroud)
查询计划位于https://www.brentozar.com/pastetheplan/?id=BkpZU1MZE,我的查询持续时间为 4:37 分钟,返回 26612 行。
我以前从未见过从现有表创建这种类型的常量扫描 - 我不熟悉为什么对每一行都运行常量扫描,而常量扫描通常用于用户输入的单行例如 SELECT GETDATE()。为什么用在这里?我非常感谢阅读此查询计划的一些指导。
如果我将 OR 拆分为 UNION,它会生成一个标准计划,运行 12 秒,返回相同的 26612 行。
SELECT Users.Id
FROM dbo.Users
INNER JOIN dbo.Posts
ON Users.Id = Posts.OwnerUserId
WHERE Users.Reputation = 5
UNION
SELECT Users.Id
FROM dbo.Users
INNER JOIN dbo.Posts
ON Users.Id = Posts.LastEditorUserId
WHERE Users.Reputation = 5
Run Code Online (Sandbox Code Playgroud)
我将这个计划解释为这样做:
Mar*_*ith 10
该计划类似于我在此处更详细介绍的计划。
这 Posts表被扫描。
对于每一行,它提取OwnerUserId和LastEditorUserId。这与工作方式类似UNPIVOT。您会在下面的计划中看到一个常量扫描运算符,为每个输入行创建两个输出行。
SELECT *
FROM dbo.Posts
UNPIVOT (X FOR U IN (OwnerUserId,LastEditorUserId)) Unpvt
Run Code Online (Sandbox Code Playgroud)
在这种情况下,计划有点复杂,因为它的语义or是如果两个列值相同,则连接中只应发出一行Users(而不是两行)
这些然后通过合并间隔,以便在值相同的情况下,范围被折叠起来,并且只对它执行一次搜索 Users- 否则对其执行两次搜索。
价值 62是一个标志,意味着该搜索应该是一个平等搜索。
关于
当它没有完成从 IX_NC_REPUTATION 到包含 Expr1010 和 Expr1011 的子树的嵌套循环连接时,我不明白它是如何访问这些的
这些是在黄色突出显示的连接运算符中定义的。这是在黄色突出显示的嵌套循环的外侧。所以这在嵌套循环内部的黄色突出显示之前运行。
提供类似计划的重写(尽管合并间隔被合并联合替换)如下以防万一。
SELECT DISTINCT D2.UserId
FROM dbo.Posts p
CROSS APPLY (SELECT Users.Id AS UserId
FROM (SELECT p.OwnerUserId
UNION /*collapse duplicate to single row*/
SELECT p.LastEditorUserId) D1(UserId)
JOIN Users
ON Users.Id = D1.UserId) D2
OPTION (FORCE ORDER)
Run Code Online (Sandbox Code Playgroud)
根据Posts表上可用的索引,此查询的变体可能比您提出的UNION ALL解决方案更有效。(我拥有的数据库副本对此没有有用的索引,建议的解决方案对 进行了两次完整扫描Posts。以下是一次扫描)
WITH Unpivoted AS
(
SELECT UserId
FROM dbo.Posts
UNPIVOT (UserId FOR U IN (OwnerUserId,LastEditorUserId)) Unpivoted
)
SELECT DISTINCT Users.Id
FROM dbo.Users INNER HASH JOIN Unpivoted
ON Users.Id = Unpivoted.UserId
WHERE Users.Reputation = 5
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
579 次 |
| 最近记录: |