Création d'un index non clusterisé sur une colonne calculée non persistante SQL Server

10

J'ai du mal à trouver une documentation sur la façon dont SQL Server stocke réellement une colonne calculée non persistante.

Prenons l'exemple suivant:

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

J'obtiens qu'il est stocké au niveau de la feuille, mais si la valeur n'est pas persistante, comment est-ce que quoi que ce soit est stocké? Comment l'index aide-t-il SQL Server à trouver ces lignes dans cette situation?

Toute aide grandement appréciée,

Merci beaucoup,

ÉDITER:

Merci à Brent & Aaron d'avoir répondu à cette question, voici le PasteThePlan montrant clairement ce qu'ils ont expliqué.

Uberzen1
la source
5
Il n'est pas conservé dans les pages de données de la table, mais il est conservé dans les pages de l' index .
Aaron Bertrand
Les colonnes calculées non persistantes ne sont pas physiquement stockées dans la table. Ce sont des colonnes virtuelles. Leurs valeurs sont recalculées chaque fois qu'elles sont référencées dans une requête. voir cette réf .
Kin Shah

Réponses:

11

Lorsque SQL Server crée l'index sur le champ calculé, le champ calculé est écrit sur le disque à ce moment-là, mais uniquement sur les 8 000 pages de cet index. SQL Server peut calculer l'InvoiceStatusID lors de la lecture de l'index clusterisé - il n'est pas nécessaire d'écrire ces données dans l'index clusterisé.

Lorsque vous supprimez / mettez à jour / insérez des lignes dans dbo.Invoice, les données des index sont tenues à jour. (Lorsque InvoiceStatus change, SQL Server sait également mettre à jour IX_Invoice.)

La meilleure façon de voir cela par vous-même est de le faire: créez ces objets et exécutez des mises à jour qui touchent le champ InvoiceStatusID. Publiez le plan d'exécution (PasteThePlan.com est utile pour cela) si vous voulez de l'aide pour voir où les mises à jour d'index se produisent.

Brent Ozar
la source
1
@ Uberzen1 Non, comme il l'a expliqué, il est écrit dans les pages d'index au moment de l'insertion / mise à jour. Il n'a rien à recalculer si l'index est utilisé pour accéder à la colonne.
Aaron Bertrand
Ah! Je suis avec toi maintenant, désolé!
Uberzen1
6
@blobbles bien, pas d'offense, mais je ne pense pas que ce soit sur Brent. Ils pourraient coller ce même XML dans Dropbox, les forums MSDN, ici, pratiquement n'importe où en ligne ... Est-ce que chaque service en ligne doit maintenant être responsable des secrets qui pourraient être divulgués par les personnes qui y téléchargent des fichiers?
Aaron Bertrand
2
@blobbles ouais, vous ne pouvez pas empêcher les gens de partager trop. Au fait, suivez-moi sur Instagram - je m'appelle BrentO - et j'y partage des photos de mon petit-déjeuner. ;-)
Brent Ozar
4
@blobbles dans le lien Confidentialité, il indique: Les données que vous copiez / collez ici sont publiques . Tout le monde peut le lire. Il n'y a aucune sécurité.
ypercubeᵀᴹ
8

La valeur d'une colonne calculée indexée et non persistante n'est pas persistante dans les pages de données de la table , mais elle est persistante dans les pages de l' index . Elle reste non persistante dans la table, qu'elle soit persistante dans 0, 1 ou plusieurs index.

Pour illustrer la description de Brent, en prenant l'exemple que vous avez donné, insérons une ligne:

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

Voyons maintenant les pages d'index:

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

(Évidemment, changez dbname, et l'ID d'index peut ne pas être 2 dans votre cas.)

Sortie (la vôtre sera sûrement différente):

entrez la description de l'image ici

Et enfin, examinons la page pour PageType2:

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

(Vous devrez probablement changer 7 pour correspondre à votre ID de base de données, et si vous avez plusieurs fichiers de données, vous devrez peut-être modifier le deuxième argument pour qu'il corresponde PageFIDau premier résultat.)

Production:

entrez la description de l'image ici

C'est sur la page d'index.

Aaron Bertrand
la source
Très cool, merci Aaron. La raison pour laquelle j'ai posé la question au départ est que j'ai vraiment du mal à déployer un index similaire dans le monde réel et que je voulais comprendre exactement ce qui se passe sous le capot afin que je puisse comprendre le problème. Cela aide beaucoup, merci!
Uberzen1
1
@ Uberzen1 Pouvez-vous définir un "vrai problème"? Allez-vous publier une question sur ce problème?
Aaron Bertrand
Je peux le faire, j'allais creuser un peu plus moi-même d'abord, mais je voulais juste comprendre ce que fait exactement l'instruction create index. Le TLDR est; J'ai un grand tableau similaire au tableau des factures ci-dessus, il a environ 400 millions d'enregistrements et, malheureusement, la colonne OrderStatus a été giflée juste au milieu, ce qui rend l'indexation, etc. un peu pénible. Nous avons ajouté une colonne calculée pour l'instant que nous finirons par persister et déplacer le champ varchar vers sa propre table. 1/2
Uberzen1
5
@ Uberzen1 Oui, parce que la colonne calculée est réellement matérialisée sur le disque lors de l'écriture dans l'index, toute cette activité doit être enregistrée. Une solution de contournement pourrait être d'arrêter de s'appuyer sur la colonne calculée - soit de mettre cette expression dans une vue ou les requêtes ad hoc, et si ce n'est pas une option, vous pouvez créer une nouvelle colonne nullable, la mettre à jour par morceaux (pour éviter la destruction du journal) , puis supprimez la colonne calculée, renommez la nouvelle colonne et modifiez votre DML pour l'écrire manuellement. Mais vraiment, comme il s'agit d'informations redondantes que vous pouvez dériver de données existantes, j'opterais pour la première option.
Aaron Bertrand
2
Merci beaucoup Aaron. Je suis heureux que vous ayez mentionné avoir un avis devant elle car c'était aussi ma solution, peut-être est-il temps de revoir cette idée!
Uberzen1
7

L'attribut PERSISTEDd'une colonne calculée est lié à la persistance des valeurs dans la table (index cluster ou segment de mémoire) et non à la persistance des valeurs dans l'index.

Le CREATE INDEXa les exigences pour les limitations concernant les colonnes et les index calculés:

Les colonnes calculées qui sont déterministes et précises ou imprécises peuvent être des colonnes incluses. Les colonnes calculées dérivées des types de données image, ntext, text, varchar (max), nvarchar (max), varbinary (max) et xml peuvent être incluses dans des colonnes non clés tant que les types de données de la colonne calculée sont autorisés comme inclus colonne. Pour plus d'informations, consultez Index sur les colonnes calculées.

Il n'y a aucune limitation quant à la persistance ou non de la colonne calculée.

et plus loin (pas sur les colonnes incluses mais sur les colonnes calculées dans la partie principale d'un index):

Les index peuvent être créés sur des colonnes calculées. De plus, les colonnes calculées peuvent avoir la propriété PERSISTED. Cela signifie que le moteur de base de données stocke les valeurs calculées dans la table et les met à jour lorsque toute autre colonne dont dépend la colonne calculée est mise à jour. Le moteur de base de données utilise ces valeurs persistantes lorsqu'il crée un index sur la colonne et lorsque l'index est référencé dans une requête.

Pour indexer une colonne calculée, la colonne calculée doit (être) déterministe et précise. Cependant, l'utilisation de la PERSISTEDpropriété étend le type de colonnes calculées indexables pour inclure:

...

ypercubeᵀᴹ
la source