加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长学院 > MsSql教程 > 正文

sql-server – 这个Constant Scan和Left Outer Join来自一个简单

发布时间:2020-12-12 06:19:22 所属栏目:MsSql教程 来源:网络整理
导读:我有这张桌子: CREATE TABLE [dbo].[Accounts] ( [AccountId] UNIQUEIDENTIFIER UNIQUE NOT NULL DEFAULT NEWID(),-- WHATEVER other columns);GOCREATE UNIQUE CLUSTERED INDEX [AccountsIndex] ON [dbo].[Accounts]([AccountId] ASC);GO 这个查询: DECLAR
我有这张桌子:
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;

执行计划

没有行到达根节点,因此不会发生任何分配.

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)
> 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.

进一步阅读:

Fun with Aggregates

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读