Quand les colonnes calculées sont-elles calculées?


29

Quand les valeurs des colonnes calculées sont-elles déterminées?

  • Lorsque la valeur est récupérée?
  • Lorsque la valeur est modifiée?
  • Une autre fois?

Je suppose que c'est une question novice car je ne trouve rien dans mes recherches.

Réponses:


19

Cela dépend de la façon dont vous définissez la colonne calculée. Une PERSISTEDcolonne calculée sera calculée puis stockée en tant que données à l'intérieur du tableau. Si vous ne définissez pas la colonne comme PERSISTED, elle sera calculée lors de l'exécution de votre requête.

Veuillez voir la réponse d' Aaron pour une grande explication et preuve.

Pinal Dave décrit également cela en détail et montre une preuve de stockage dans sa série:

SERVEUR SQL - Colonne calculée - PERSISTE et stockage


6
Qu'en est-il s'ils persistent, mais que le plan de requête utilise un index qui ne couvre pas cette colonne? Je ne sais pas si vous obtiendriez une recherche ou si elle serait simplement calculée à la volée et ne peut pas actuellement la tester.
Martin Smith

1
@Martin, vous avez raison, dans mon test, SQL Server a choisi de recalculer plutôt qu'une recherche.
Aaron Bertrand

34

C'est très facile à prouver par vous-même. Nous pouvons créer une table avec une colonne calculée qui utilise une fonction définie par l'utilisateur scalaire, puis vérifier les plans et les statistiques de fonction avant et après une mise à jour et sélectionner, et voir quand une exécution est enregistrée.

Disons que nous avons cette fonction:

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

Et ce tableau:

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

Vérifions sys.dm_exec_function_stats(nouveau dans SQL Server 2016 et Azure SQL Database) avant et après une insertion, puis après une sélection:

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();

Je ne vois aucun appel de fonction sur l'insert, uniquement sur la sélection.

Maintenant, déposez les tables et recommencez, en changeant cette fois la colonne en PERSISTED:

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

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

Et je vois le contraire se produire: j'obtiens une exécution enregistrée sur l'insert, mais pas sur la sélection.

Vous n'avez pas de version suffisamment moderne de SQL Server à utiliser sys.dm_exec_function_stats? Pas de soucis, cela est également reflété dans les plans d'exécution .

Pour la version non persistante, nous pouvons voir la fonction référencée uniquement dans la sélection:

entrez la description de l'image ici

entrez la description de l'image ici

Alors que la version persistante ne montre que le calcul se produisant lors de l'insertion:

entrez la description de l'image ici

entrez la description de l'image ici

Maintenant, Martin soulève un grand point dans un commentaire : ce ne sera pas toujours vrai. Créons un index qui ne couvre pas la colonne calculée persistante, et exécutons une requête qui utilise cet index, et voyons si la recherche obtient les données des données persistantes existantes, ou calcule les données au moment de l'exécution (fonction de suppression et de recréation et tableau ici):

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

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

Maintenant, nous allons exécuter une requête qui utilise l'index (en fait, il utilise l'index par défaut dans ce cas spécifique de toute façon, même sans clause where):

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

Je vois des exécutions supplémentaires dans les statistiques de fonction, et le plan ne ment pas:

entrez la description de l'image ici

Donc, la réponse est IT DEPENDS . Dans ce cas, SQL Server pensait qu'il serait moins coûteux de recalculer les valeurs que d'effectuer des recherches. Cela pourrait changer en raison de divers facteurs, alors ne vous y fiez pas. Et cela peut se produire dans les deux sens, qu'une fonction définie par l'utilisateur soit utilisée ou non; Je ne l'ai utilisé ici que parce que c'était plus facile à illustrer.


Très apprécié, je n'ai jamais remis en cause le comportement du moteur dans le calcul des résultats.
Arthur D

8
@ArthurD C'est une décision d'optimisation basée (principalement) sur les coûts estimés de chaque alternative, voir ma réponse à une autre question ici.
Paul White dit GoFundMonica

-1

La réponse à cette question est vraiment "cela dépend". Je viens de parcourir un exemple où SQL Server utilise l'index sur la colonne calculée persistante mais il exécute toujours la fonction, comme si les valeurs n'étaient jamais persistantes pour commencer. Cela peut avoir à voir avec le type de données de la colonne ( nvarchar(37)) ou peut-être la taille de la table (environ 7 millions de lignes), mais SQL Server a décidé d'ignorer le persistedmot clé, semble-t-il, dans ce cas particulier.

Dans ce cas, la clé primaire de la table est TransactionID, qui est également une colonne calculée et persistante. Le plan d'exécution génère un balayage d'index et dans une table avec seulement 7 millions de lignes, cette requête simple prend plus de 2 à 3 minutes pour s'exécuter car la fonction est exécutée à nouveau sur chaque ligne et les valeurs ne semblent pas être persistées dans l'index.

création d'une table avec une colonne persistante plan d'exécution montrant que la fonction est en cours d'exécution

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.