Réponses:
Cela dépend de la façon dont vous définissez la colonne calculée. Une PERSISTED
colonne 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:
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:
Alors que la version persistante ne montre que le calcul se produisant lors de l'insertion:
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:
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.
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 persisted
mot 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.