sql-server – 这个Constant Scan和Left Outer Join来自一个简单
我有这张桌子:
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 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |