Warum berechnet SQL Server den Skalarwert, wenn ich eine beständige berechnete Spalte auswähle?

21

Die drei SELECTAnweisungen in diesem Code

USE [tempdb];
GO

SET NOCOUNT ON;

CREATE TABLE dbo.persist_test (
      id            INT NOT NULL
    , id5           AS (id * 5)
    , id5p          AS (id * 5) PERSISTED
);

INSERT INTO dbo.persist_test (id)
VALUES (1), (2), (3);

SELECT id
FROM dbo.persist_test;

SELECT id5
FROM dbo.persist_test;

SELECT id5p
FROM dbo.persist_test;

DROP TABLE dbo.persist_test;

Generieren Sie diesen Plan:

Ausführungsplan

Warum SELECTgeneriert das Finale , das einen dauerhaften Wert auswählt, einen Compute Scalar- Operator?

Nick Chammas
quelle
3
Siehe @ SqlKiwis Antwort hier: Warum enthält der Ausführungsplan einen benutzerdefinierten Funktionsaufruf für eine berechnete Spalte, die beibehalten wird? . In Ihrer Abfrage wird nur die Liste der Ausgabespalten aus der Tabelle angezeigt [tempdb].[dbo].[persist_test].idund der Wert berechnet, obwohl er beibehalten wird.
Martin Smith

Antworten:

14

Nur um die experimentellen Ergebnisse in den Kommentaren zusammenzufassen, scheint dies ein Randfall zu sein, der auftritt, wenn Sie zwei berechnete Spalten in derselben Tabelle haben, eine persistedund eine nicht beibehalten und beide dieselbe Definition haben.

Im Plan für die Abfrage

SELECT id5p
FROM dbo.persist_test;

Der Table Scan on persist_testgibt nur die idSpalte aus. Der nächste Berechnungsskalar multipliziert dies mit 5 und gibt eine aufgerufene Spalte aus, id5obwohl in der Abfrage nicht einmal auf diese Spalte verwiesen wird. Der endgültige Berechnungsskalar nimmt den Wert von id5und gibt diesen als aufgerufene Spalte aus id5p.

Verwenden der in Query Optimizer Deep Dive - Teil 2 erläuterten Ablaufverfolgungsflags (Haftungsausschluss: Diese Ablaufverfolgungsflags werden nicht dokumentiert / nicht unterstützt) und Betrachten der Abfrage

SELECT id5,
       id5p,
       ( id * 5 )
FROM   dbo.persist_test 
OPTION (QUERYTRACEON 3604, QUERYTRACEON 8606);

Gibt die Ausgabe aus

Baum vor Projektnormalisierung

LogOp_Project

    LogOp_Get TBL: dbo.persist_test dbo.persist_test TableID=1717581157 TableReferenceID=0 IsRow: COL: IsBaseRow1002 

    AncOp_PrjList 

        AncOp_PrjEl QCOL: [tempdb].[dbo].[persist_test].id5

            ScaOp_Arithmetic x_aopMult

                ScaOp_Identifier QCOL: [tempdb].[dbo].[persist_test].id

                ScaOp_Const TI(int,ML=4) XVAR(int,Not Owned,Value=5)

        AncOp_PrjEl QCOL: [tempdb].[dbo].[persist_test].id5p

            ScaOp_Arithmetic x_aopMult

                ScaOp_Identifier QCOL: [tempdb].[dbo].[persist_test].id

                ScaOp_Const TI(int,ML=4) XVAR(int,Not Owned,Value=5)

        AncOp_PrjEl COL: Expr1004 

            ScaOp_Arithmetic x_aopMult

                ScaOp_Identifier QCOL: [tempdb].[dbo].[persist_test].id

                ScaOp_Const TI(int,ML=4) XVAR(int,Not Owned,Value=5)

Baum nach Projektnormalisierung

LogOp_Project

    LogOp_Get TBL: dbo.persist_test dbo.persist_test TableID=1717581157 TableReferenceID=0 IsRow: COL: IsBaseRow1002 

    AncOp_PrjList 

        AncOp_PrjEl QCOL: [tempdb].[dbo].[persist_test].id5

            ScaOp_Identifier QCOL: [tempdb].[dbo].[persist_test].id5

        AncOp_PrjEl QCOL: [tempdb].[dbo].[persist_test].id5p

            ScaOp_Identifier QCOL: [tempdb].[dbo].[persist_test].id5

        AncOp_PrjEl COL: Expr1004 

            ScaOp_Identifier QCOL: [tempdb].[dbo].[persist_test].id5

Es sieht also so aus, als würden alle berechneten Spaltendefinitionen während der Phase der Projektnormalisierung erweitert und alle identischen Ausdrücke wieder mit berechneten Spalten abgeglichen, was id5in diesem Fall einfach zutrifft . dh die persistedSpalte wird nicht bevorzugt .

Wenn die Tabelle mit der folgenden Definition neu erstellt wird

CREATE TABLE dbo.persist_test (
      id            INT NOT NULL
    , id5p          AS (5 * id) PERSISTED
    , id5           AS (5 * id)
);

Dann wird eine Anfrage für entweder id5oder id5pwird beim Lesen der persistente Version der Daten erfüllt werden , anstatt die Berechnung zur Laufzeit zu tun , um die passenden (zumindest in diesem Fall) zu passieren scheint in Spaltenreihenfolge.

Martin Smith
quelle