Erstellen eines nicht gruppierten Index für nicht persistenten SQL Server mit berechneten Spalten

10

Ich habe Probleme, eine Dokumentation darüber zu finden, wie SQL Server eine nicht persistierte berechnete Spalte tatsächlich speichert.

Nehmen Sie das folgende Beispiel:

--SCHEMA
CREATE TABLE dbo.Invoice
(
    InvoiceID INT IDENTITY(1, 1) PRIMARY KEY,
    CustomerID INT FOREIGN KEY REFERENCES dbo.Customer(CustomerID),
    InvoiceStatus NVARCHAR(50) NOT NULL,
    InvoiceStatusID AS CASE InvoiceStatus 
                         WHEN 'Sent' THEN 1 
                         WHEN 'Complete' THEN 2
                         WHEN 'Received' THEN 3
                       END
)
GO

--INDEX
CREATE NONCLUSTERED INDEX IX_Invoice ON Invoice
(
    CustomerID ASC
)
INCLUDE
(
    InvoiceStatusID
)
GO

Ich verstehe, dass es auf Blattebene gespeichert ist, aber wenn der Wert nicht beibehalten wird, wie wird überhaupt etwas gespeichert? Wie hilft der Index SQL Server in dieser Situation, diese Zeilen zu finden?

Jede Hilfe sehr geschätzt,

Danke vielmals,

BEARBEITEN:

Vielen Dank an Brent & Aaron für die Beantwortung dieser Frage. Hier ist der PasteThePlan, der deutlich zeigt, was sie erklärt haben.

Uberzen1
quelle
5
Es wird nicht auf den Datenseiten der Tabelle beibehalten, sondern auf den Seiten des Index .
Aaron Bertrand
Nicht persistierte berechnete Spalten werden nicht physisch in der Tabelle gespeichert. Sie sind virtuelle Spalten. Ihre Werte werden jedes Mal neu berechnet, wenn in einer Abfrage auf sie verwiesen wird. siehe diese ref .
Kin Shah

Antworten:

11

Wenn SQL Server den Index für das berechnete Feld erstellt, wird das berechnete Feld zu diesem Zeitpunkt auf die Festplatte geschrieben - jedoch nur auf den 8-KB-Seiten dieses Index. SQL Server kann die InvoiceStatusID beim Lesen des Clustered-Index berechnen. Diese Daten müssen nicht in den Clustered-Index geschrieben werden.

Beim Löschen / Aktualisieren / Einfügen von Zeilen in dbo.Invoice werden die Daten in den Indizes auf dem neuesten Stand gehalten. (Wenn sich InvoiceStatus ändert, kann SQL Server auch IX_Invoice aktualisieren.)

Der beste Weg, dies selbst zu sehen, besteht darin, dies tatsächlich zu tun: Erstellen Sie diese Objekte und führen Sie Aktualisierungen aus, die das Feld InvoiceStatusID berühren. Veröffentlichen Sie den Ausführungsplan (PasteThePlan.com ist hierfür hilfreich), wenn Sie Hilfe benötigen, um zu sehen, wo die Indexaktualisierungen stattfinden.

Brent Ozar
quelle
1
@ Uberzen1 Nein, wie er erklärte, wird es beim Einfügen / Aktualisieren auf die Indexseiten geschrieben. Es muss nichts neu berechnet werden, wenn der Index für den Zugriff auf die Spalte verwendet wird.
Aaron Bertrand
Ah! Ich bin jetzt bei dir, sorry!
Uberzen1
6
@blobbles gut, nichts für ungut, aber ich glaube nicht, dass das auf Brent liegt. Sie könnten dasselbe XML in Dropbox- und MSDN-Foren einfügen, hier, praktisch überall online ... Muss jetzt jeder Onlinedienst für Geheimnisse verantwortlich sein, die von Personen preisgegeben werden könnten, die dort Dateien hochladen?
Aaron Bertrand
2
@blobbles Ja, du kannst die Leute einfach nicht davon abhalten, zu viel zu teilen. Hey, folge mir übrigens auf Instagram - ich bin BrentO - und ich teile dort Bilder von meinem Frühstück. ;-)
Brent Ozar
4
@blobbles im Datenschutz-Link heißt es: Daten, die Sie hier kopieren / einfügen, sind öffentlich . Jeder kann es lesen. Es gibt keine Sicherheit.
Ypercubeᵀᴹ
8

Der Wert für eine indizierte, nicht persistierte berechnete Spalte wird nicht auf den Datenseiten der Tabelle , sondern auf den Seiten des Index beibehalten . Es bleibt in der Tabelle nicht erhalten, unabhängig davon, ob es in 0, 1 oder mehreren Indizes beibehalten wird.

Um die Beschreibung von Brent anhand des von Ihnen angegebenen Beispiels zu veranschaulichen, fügen wir eine Zeile ein:

INSERT dbo.Invoice(CustomerID, InvoiceStatus) VALUES(1,N'Sent');

Schauen wir uns nun die Indexseiten an:

DBCC TRACEON(3604, -1);
DBCC IND(N'dbname', N'dbo.Invoice', 2);

(Offensichtlich ändern dbname, und die Index-ID ist in Ihrem Fall möglicherweise nicht 2.)

Ausgabe (Ihre wird sich sicherlich unterscheiden):

Geben Sie hier die Bildbeschreibung ein

Und zum Schluss untersuchen wir die Seite auf PageType2:

DBCC PAGE(7, 1, 584, 3);

(Sie müssen wahrscheinlich 7 ändern, um mit Ihrer Datenbank-ID übereinzustimmen. Wenn Sie mehrere Datendateien haben, müssen Sie möglicherweise das zweite Argument so ändern, dass es mit PageFIDdem ersten Ergebnis übereinstimmt .)

Ausgabe:

Geben Sie hier die Bildbeschreibung ein

Das steht auf der Indexseite.

Aaron Bertrand
quelle
Sehr cool, danke Aaron. Der Grund, warum ich die Frage anfangs gestellt habe, ist, dass ich echte Probleme habe, einen ähnlichen Index in der realen Welt bereitzustellen, und genau verstehen wollte, was unter der Haube vor sich geht, damit ich das Problem herausfinden kann. Das hilft sehr, danke!
Uberzen1
1
@ Uberzen1 Kannst du "echte Probleme" definieren? Wirst du eine Frage zu diesem Problem posten ?
Aaron Bertrand
Ich könnte es tun, ich wollte mich zuerst noch etwas damit befassen, wollte aber nur herausfinden, was die Anweisung create index genau tut. Der TLDR ist; Ich habe eine große Tabelle ähnlich der obigen Rechnungstabelle, sie hat ungefähr 400 Millionen Datensätze und leider ist die OrderStatus-Spalte genau in der Mitte geklatscht, was die Indizierung usw. etwas schmerzhaft macht. Wir haben vorerst eine berechnete Spalte hinzugefügt, damit wir das varchar-Feld schließlich beibehalten und in eine eigene Tabelle verschieben können. 1/2
Uberzen1
5
@ Uberzen1 Ja, da die berechnete Spalte beim Schreiben in den Index tatsächlich auf der Festplatte materialisiert wird, muss all diese Aktivität protokolliert werden. Eine Problemumgehung könnte darin bestehen, sich nicht mehr auf die berechnete Spalte zu verlassen. Fügen Sie diesen Ausdruck entweder in eine Ansicht oder in die Ad-hoc-Abfragen ein. Wenn dies keine Option ist, können Sie eine neue nullfähige Spalte erstellen und in Blöcken aktualisieren (um das Beenden des Protokolls zu vermeiden.) Löschen Sie dann die berechnete Spalte, benennen Sie die neue Spalte um und ändern Sie Ihre DML so, dass sie manuell geschrieben wird. Aber da es sich um redundante Informationen handelt, die Sie aus vorhandenen Daten ableiten können, würde ich mich für die erste Option entscheiden.
Aaron Bertrand
2
Vielen Dank Aaron. Ich bin froh, dass Sie erwähnt haben, dass Sie eine Ansicht davor haben, da dies auch meine Lösung war. Vielleicht ist es an der Zeit, diese Idee zu überdenken!
Uberzen1
7

Das Attribut PERSISTEDfür eine berechnete Spalte bezieht sich darauf, ob die Werte in der Tabelle (Clustered-Index oder Heap) beibehalten werden und nicht, ob die Werte im Index beibehalten werden.

Das CREATE INDEXhat die Anforderungen für die Einschränkungen in Bezug auf berechnete Spalten und Indizes:

Berechnete Spalten, die deterministisch und entweder präzise oder ungenau sind, können Spalten enthalten. Berechnete Spalten, die aus den Datentypen image, ntext, text, varchar (max), nvarchar (max), varbinary (max) und xml abgeleitet wurden, können in Nicht-Schlüsselspalten enthalten sein, sofern die berechneten Spaltendatentypen als eingeschlossen zulässig sind Säule. Weitere Informationen finden Sie unter Indizes für berechnete Spalten.

Es gibt keine Einschränkung, ob die berechnete Spalte beibehalten wird oder nicht.

und weiter (nicht über eingeschlossene, sondern über berechnete Spalten im Hauptteil eines Index):

Indizes können für berechnete Spalten erstellt werden. Darüber hinaus können berechnete Spalten die Eigenschaft haben PERSISTED. Dies bedeutet, dass das Datenbankmodul die berechneten Werte in der Tabelle speichert und sie aktualisiert, wenn andere Spalten, von denen die berechnete Spalte abhängt, aktualisiert werden. Das Datenbankmodul verwendet diese beibehaltenen Werte, wenn es einen Index für die Spalte erstellt und wenn auf den Index in einer Abfrage verwiesen wird.

Um eine berechnete Spalte zu indizieren, muss die berechnete Spalte deterministisch und präzise sein. Durch die Verwendung der PERSISTEDEigenschaft wird der Typ der indizierbaren berechneten Spalten jedoch um Folgendes erweitert:

...

ypercubeᵀᴹ
quelle