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
什么是额外的魔力?左外连接后的恒定扫描有什么作用?
解决方法
>如果没有找到行,则第一个不设置变量的值.
>第二个总是设置变量,如果没有找到行,则包括null.
常量扫描生成一个空行(没有列!),如果没有与基表匹配的情况,将导致变量被更新.左连接确保空行在连接中存活.变量赋值可以被认为是在执行计划的根节点处发生的.
使用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;
执行计划
额外的恒定扫描和嵌套循环左外连接无需关注.特别是连接是便宜的,因为它保证在其外部输入上遇到一行,并且在内部输入上最多只有一行(在您的示例中).
还有其他方法可以确保从子查询生成行以确保发生变量赋值.一种是使用冗余标量聚合(没有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)
> SELECT @local_variable (Transact-SQL)
If the SELECT statement returns no rows,the variable retains its present value. If expression is a scalar subquery that returns no value,the variable is set to NULL.
For assigning variables,we recommend that you use SET @local_variable instead of SELECT @local_variable.
进一步阅读: