sha*_*oth 23 sql-server execution-plan azure-sql-database
我有这张桌子:
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
Run Code Online (Sandbox Code Playgroud)
这个查询:
DECLARE @result UNIQUEIDENTIFIER
SELECT @result = AccountId FROM Accounts WHERE AccountId='guid-here'
Run Code Online (Sandbox Code Playgroud)
使用由单个索引查找组成的查询计划执行 - 正如预期的那样:
SELECT <---- Clustered Index Seek
Run Code Online (Sandbox Code Playgroud)
此查询执行相同的操作:
DECLARE @result UNIQUEIDENTIFIER
SET @result = (SELECT AccountId FROM Accounts WHERE AccountId='guid-here')
Run Code Online (Sandbox Code Playgroud)
但它是按照一个计划执行的,其中索引查找的结果与一些常量扫描的结果左外连接,然后输入计算标量:
SELECT <--- Compute Scalar <--- Left Outer Join <--- Constant Scan
^
|------Clustered Index Seek
Run Code Online (Sandbox Code Playgroud)
什么是额外的魔法?常量扫描后跟左外连接有什么作用?
Pau*_*ite 29
这两个语句的语义是不同的:
常量扫描产生一个空行(没有列!),这将导致变量被更新,以防基表中没有匹配项。左连接确保空行在连接后仍然存在。可以认为变量赋值发生在执行计划的根节点。
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;
Run Code Online (Sandbox Code Playgroud)
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;
Run Code Online (Sandbox Code Playgroud)
额外的常量扫描和嵌套循环左外连接无需担心。特别是连接很便宜,因为它保证在其外部输入上遇到一行,而在内部输入上最多遇到一行(在您的示例中)。
还有其他方法可以确保从子查询生成一行以确保发生变量分配。一种是使用冗余标量聚合(没有 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;
Run Code Online (Sandbox Code Playgroud)
请注意,即使没有收到输入,标量聚合也会生成一行。
文档:
如果 SELECT 语句不返回任何行,则变量保留其当前值。如果表达式是不返回值的标量子查询,则该变量设置为 NULL。
对于分配变量,我们建议您使用 SET @local_variable 而不是 SELECT @local_variable。
进一步阅读:
| 归档时间: |
|
| 查看次数: |
1722 次 |
| 最近记录: |