sql-server – 这个Constant Scan和Left Outer Join来自一个简单的SELECT查询计
|
我有这张桌子: CREATE TABLE [dbo].[Accounts] (
[AccountId] UNIQUEIDENTIFIER UNIQUE NOT NULL DEFAULT NEWID(),-- WHATEVER other columns
);
GO
CREATE UNIQUE CLUSTERED INDEX [AccountsIndex]
ON [dbo].[Accounts]([AccountId] ASC);
GO
这个查询: DECLARE @result UNIQUEIDENTIFIER SELECT @result = AccountId FROM Accounts WHERE AccountId='guid-here' 使用由单个索引查找组成的查询计划执行 – 按预期方式: SELECT <---- Clustered Index Seek 此查询执行相同的操作: DECLARE @result UNIQUEIDENTIFIER SET @result = (SELECT AccountId FROM Accounts WHERE AccountId='guid-here') 但是它执行了一个计划,其中Index Seek的结果是左外连接与某些恒定扫描的结果,然后输入Compute Scalar: SELECT <--- Compute Scalar <--- Left Outer Join <--- Constant Scan
^
|------Clustered Index Seek
什么是额外的魔力?左外连接后的恒定扫描有什么作用? 解决方法这两个语句的语义不同:>如果没有找到行,则第一个不设置变量的值. 常量扫描生成一个空行(没有列!),如果没有与基表匹配的情况,将导致变量被更新.左连接确保空行在连接中存活.变量赋值可以被认为是在执行计划的根节点处发生的. 使用SELECT @result -- Set initial value
DECLARE @result uniqueidentifier = {guid 'FE2CA909-1162-4C6C-A7AC-33B257E28539'};
-- @result does not change
SELECT @result = AccountId
FROM Accounts
WHERE AccountId={guid '7AD4D33C-1ED7-4183-B7F3-48C33D666525'};
SELECT @result;
使用SET @result -- Set initial value
DECLARE @result uniqueidentifier = {guid 'FE2CA909-1162-4C6C-A7AC-33B257E28539'};
-- @result set to null
SET @result =
(
SELECT AccountId
FROM Accounts
WHERE AccountId={guid '7AD4D33C-1ED7-4183-B7F3-48C33D666525'}
);
SELECT @result;
执行计划 没有行到达根节点,因此不会发生任何分配. A行始终到达根节点,因此发生变量赋值. 额外的恒定扫描和嵌套循环左外连接无需关注.特别是连接是便宜的,因为它保证在其外部输入上遇到一行,并且在内部输入上最多只有一行(在您的示例中). 还有其他方法可以确保从子查询生成行以确保发生变量赋值.一种是使用冗余标量聚合(没有group by子句): -- Set initial value
DECLARE @result uniqueidentifier = {guid 'FE2CA909-1162-4C6C-A7AC-33B257E28539'};
-- @result set to null
SET @result =
(
SELECT MAX(AccountId)
FROM Accounts
WHERE AccountId={guid '7AD4D33C-1ED7-4183-B7F3-48C33D666525'}
);
SELECT @result;
请注意,标量聚合生成一行,即使它没有收到任何输入. 文档: > SET @local_variable (Transact-SQL)
进一步阅读: Fun with Aggregates (编辑:安卓应用网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
