Wann werden berechnete Spalten berechnet?

29

Wann werden die Werte für berechnete Spalten ermittelt?

  • Wann wird der Wert abgerufen?
  • Wann wird der Wert geändert?
  • Ein andermal?

Ich vermute, dies ist eine unerfahrene Frage, da ich bei meinen Suchen nichts finde.

Shelby115
quelle

Antworten:

19

Dies hängt davon ab, wie Sie die berechnete Spalte definieren. Eine PERSISTEDberechnete Spalte wird berechnet und dann als Daten in der Tabelle gespeichert. Wenn Sie die Spalte nicht als definieren PERSISTED, wird sie beim Ausführen Ihrer Abfrage berechnet.

Bitte sehen Sie Aarons Antwort für eine gute Erklärung und einen Beweis.

Pinal Dave beschreibt dies ebenfalls ausführlich und zeigt den Nachweis der Lagerung in seiner Serie:

SQL Server - Computed Column - PERSISTED und Speicher

Arthur D
quelle
6
Was ist, wenn sie bestehen bleiben, der Abfrageplan jedoch einen Index verwendet, der diese Spalte nicht abdeckt? Ich bin mir nicht sicher, ob Sie einen Lookup erhalten würden oder ob er nur im laufenden Betrieb berechnet würde und ihn derzeit nicht testen kann.
Martin Smith
1
@ Martin Sie haben Recht, in meinem Test entschied sich SQL Server für eine Neuberechnung über eine Suche.
Aaron Bertrand
34

Dies ist sehr einfach selbst zu beweisen. Wir können eine Tabelle mit einer berechneten Spalte erstellen, die eine skalare benutzerdefinierte Funktion verwendet, und dann Pläne und Funktionsstatistiken vor und nach einer Aktualisierung und Auswahl überprüfen und sehen, wann eine Ausführung aufgezeichnet wird.

Nehmen wir an, wir haben diese Funktion:

CREATE FUNCTION dbo.mask(@x varchar(32))
RETURNS varchar(32) WITH SCHEMABINDING
AS
BEGIN
  RETURN (SELECT 'XX' + SUBSTRING(@x, 3, LEN(@x)-4) + 'XXXX');
END
GO

Und dieser Tisch:

CREATE TABLE dbo.Floobs
(
  FloobID int IDENTITY(1,1),
  Name varchar(32),
  MaskedName AS CONVERT(varchar(32), dbo.mask(Name)),
  CONSTRAINT pk_Floobs PRIMARY KEY(FloobID),
  CONSTRAINT ck_Name CHECK (LEN(Name)>=8)
);
GO

Überprüfen wir sys.dm_exec_function_stats(neu in SQL Server 2016 und Azure SQL-Datenbank) vor und nach dem Einfügen und anschließend nach einer Auswahl:

SELECT o.name, s.execution_count
FROM sys.dm_exec_function_stats AS s
INNER JOIN sys.objects AS o
ON o.[object_id] = s.[object_id]
WHERE s.database_id = DB_ID();

INSERT dbo.Floobs(Name) VALUES('FrankieC');

SELECT o.name, s.execution_count
FROM sys.dm_exec_function_stats AS s
INNER JOIN sys.objects AS o
ON o.[object_id] = s.[object_id]
WHERE s.database_id = DB_ID();

SELECT * FROM dbo.Floobs;

SELECT o.name, s.execution_count
FROM sys.dm_exec_function_stats AS s
INNER JOIN sys.objects AS o
ON o.[object_id] = s.[object_id]
WHERE s.database_id = DB_ID();

Ich sehe keinen Funktionsaufruf am Insert, nur am Select.

Löschen Sie nun die Tabellen und wiederholen Sie den Vorgang. Ändern Sie diesmal die Spalte in PERSISTED:

DROP TABLE dbo.Floobs;
GO
DROP FUNCTION dbo.mask;
GO

...
  MaskedName AS CONVERT(varchar(32), dbo.mask(Name)) PERSISTED,
...

Und ich sehe das Gegenteil: Ich bekomme eine Ausführung auf dem Insert protokolliert, aber nicht auf dem Select.

Sie haben keine moderne Version von SQL Server sys.dm_exec_function_stats? Keine Sorge, dies ist auch in den Ausführungsplänen festgehalten .

Für die nicht persistente Version können wir die Funktion, auf die verwiesen wird, nur in select sehen:

Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben

Während die persistierte Version nur die Berechnung beim Einfügen anzeigt:

Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben

Jetzt bringt Martin einen wichtigen Punkt in einem Kommentar vor : Das wird nicht immer wahr sein. Erstellen wir einen Index, der die persistierte berechnete Spalte nicht abdeckt, und führen Sie eine Abfrage aus, die diesen Index verwendet, und prüfen Sie, ob bei der Suche die Daten aus den vorhandenen persistierten Daten abgerufen werden, oder berechnen Sie die Daten zur Laufzeit (Funktion zum Löschen und erneuten Erstellen) und Tabelle hier):

CREATE INDEX x ON dbo.Floobs(Name);
GO

INSERT dbo.Floobs(name) 
  SELECT LEFT(name, 32) 
  FROM sys.all_columns 
  WHERE LEN(name) >= 8;

Jetzt führen wir eine Abfrage aus, die den Index verwendet (tatsächlich wird der Index in diesem speziellen Fall standardmäßig verwendet, auch ohne eine where-Klausel):

SELECT * FROM dbo.Floobs WITH (INDEX(x))
  WHERE Name LIKE 'S%';

Ich sehe zusätzliche Ausführungen in den Funktionsstatistiken und der Plan lügt nicht:

Bildbeschreibung hier eingeben

Die Antwort ist also IT-ABHÄNGIGKEIT . In diesem Fall hielt es SQL Server für billiger, die Werte neu zu berechnen, als Suchvorgänge durchzuführen. Dies kann sich aufgrund verschiedener Faktoren ändern. Verlassen Sie sich also nicht darauf. Dies kann in beide Richtungen geschehen, unabhängig davon, ob eine benutzerdefinierte Funktion verwendet wird oder nicht. Ich habe es hier nur verwendet, weil es so viel einfacher zu veranschaulichen war.

Aaron Bertrand
quelle
Sehr geschätzt, ich habe das Verhalten der Engine bei der Berechnung von Ergebnissen nie in Frage gestellt.
Arthur D
8
@ArthurD Es handelt sich um eine Optimierungsentscheidung, die (hauptsächlich) auf den geschätzten Kosten jeder Alternative basiert. Siehe meine Antwort auf eine andere Frage hier.
Paul White sagt GoFundMonica
-1

Die Antwort auf diese Frage lautet wirklich "es kommt darauf an". Ich bin gerade auf ein Beispiel gestoßen, in dem SQL Server den Index für die persistierte berechnete Spalte verwendet, aber die Funktion weiterhin ausführt, als ob die Werte von Anfang an nie persistiert würden. Möglicherweise hat dies mit dem Datentyp der Spalte ( nvarchar(37)) oder möglicherweise mit der Größe der Tabelle (etwa 7 Millionen Zeilen) zu tun , doch SQL Server hat sich persistedin diesem speziellen Fall entschlossen, das Schlüsselwort zu ignorieren .

In diesem Fall ist der Primärschlüssel in der Tabelle "TransactionID" (Transaktions-ID), bei der es sich ebenfalls um eine berechnete und beständige Spalte handelt. Der Ausführungsplan generiert einen Index-Scan und in einer Tabelle mit nur 7 Millionen Zeilen dauert die Ausführung dieser einfachen Abfrage mehr als 2-3 Minuten, da die Funktion für jede Zeile erneut ausgeführt wird und die Werte nicht beibehalten zu werden scheinen Der Index.

Erstellen einer Tabelle mit einer dauerhaften Spalte Ausführungsplan mit Funktion wird ausgeführt

Corpulent Brony
quelle