この定数スキャンと左外部結合は、単純なSELECTクエリプランのどこから来たのですか?


21

私はこの表を持っています:

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

ただし、インデックスシークの結果が左外部結合の結果とコンスタントスキャンの結果と結合され、その後Compute Scalarに入力されるプランで実行されます。

SELECT <--- Compute Scalar <--- Left Outer Join <--- Constant Scan
                                      ^
                                      |------Clustered Index Seek

その余分な魔法は何ですか?左外部結合が続くコンスタントスキャンは何をしますか?

回答:


29

2つのステートメントのセマンティクスは異なります。

  • 1つ目は、行が見つからない場合、変数の値を設定しません。
  • 2番目は常に変数を設定し、行が見つからない場合は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;

結果1

を使用して 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;

結果2

実行計画

SELECT割り当てルートノードに行が到着しないため、割り当ては発生しません。

SET割り当て行は常にルートノードに到達するため、変数の割り当てが発生します。


余分な定数スキャンとネストされたループの左外部結合は、心配する必要はありません。結合は、外部入力で1行、内部入力で最大1行(この例では)に遭遇することが保証されているため、特に安価です。

副照会から行が生成されるようにして、変数の割り当てが行われるようにする他の方法があります。1つは、冗長なスカラー集約を使用することです(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;

結果3

スカラー集計実行計画

入力を受け取らなくても、スカラー集約が行を生成することに注意してください。

ドキュメンテーション:

SELECTステートメントが行を返さない場合、変数は現在の値を保持します。expressionが値を返さないスカラーサブクエリの場合、変数はNULLに設定されます。

変数を割り当てるには、SELECT @local_variableの代わりにSET @local_variableを使用することをお勧めします。

参考文献:

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.