Respuestas:
Depende de cómo defina la columna calculada. Se PERSISTED
calculará una columna calculada y luego se almacenará como datos dentro de la tabla. Si no define la columna como PERSISTED
, se calculará cuando se ejecute su consulta.
Por favor vea la respuesta de Aaron para una gran explicación y prueba.
Pinal Dave también describe esto en detalle y muestra una prueba de almacenamiento en su serie:
SERVIDOR SQL - Columna calculada - PERSISTADO y Almacenamiento
Esto es muy fácil de probar por su cuenta. Podemos crear una tabla con una columna calculada que use una función escalar definida por el usuario, y luego verificar los planes y las estadísticas de la función antes y después de una actualización y selección, y ver cuándo se registra una ejecución.
Digamos que tenemos esta función:
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
Y esta tabla:
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
Verifiquemos sys.dm_exec_function_stats
(nuevo en SQL Server 2016 y Azure SQL Database) antes y después de una inserción, y luego después de una selección:
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();
No veo ninguna llamada a la función en la inserción, solo en la selección.
Ahora, suelte las tablas y vuelva a hacerlo, esta vez cambiando la columna a PERSISTED
:
DROP TABLE dbo.Floobs;
GO
DROP FUNCTION dbo.mask;
GO
...
MaskedName AS CONVERT(varchar(32), dbo.mask(Name)) PERSISTED,
...
Y veo que sucede lo contrario: obtengo una ejecución registrada en la inserción, pero no en la selección.
¿No tiene una versión suficientemente moderna de SQL Server para usar sys.dm_exec_function_stats
? No se preocupe, esto también está capturado en los planes de ejecución .
Para la versión no persistente, podemos ver la función referenciada solo en select:
Si bien la versión persistente solo muestra el cálculo que ocurre en la inserción:
Ahora, Martin menciona un gran punto en un comentario : esto no siempre será cierto. Creemos un índice que no cubra la columna calculada persistente, y ejecutemos una consulta que use ese índice, y veamos si la búsqueda obtiene los datos de los datos persistentes existentes, o calcula los datos en tiempo de ejecución (función drop and re-create) y tabla aquí):
CREATE INDEX x ON dbo.Floobs(Name);
GO
INSERT dbo.Floobs(name)
SELECT LEFT(name, 32)
FROM sys.all_columns
WHERE LEN(name) >= 8;
Ahora, ejecutaremos una consulta que use el índice (de todos modos, en realidad, usa el índice en este caso específico, incluso sin una cláusula where):
SELECT * FROM dbo.Floobs WITH (INDEX(x))
WHERE Name LIKE 'S%';
Veo ejecuciones adicionales en las estadísticas de funciones, y el plan no miente:
Entonces, la respuesta es DEPENDE . En este caso, SQL Server pensó que sería más barato volver a calcular los valores que realizar búsquedas. Esto podría cambiar debido a una variedad de factores, así que no confíes en él. Y esto puede suceder en cualquier dirección, se use o no una función definida por el usuario; Solo lo usé aquí porque lo hizo mucho más fácil de ilustrar.
La respuesta a esta pregunta realmente es "depende". Acabo de encontrar un ejemplo en el que SQL Server está utilizando el índice en la columna calculada persistente, pero aún realiza la función, como si los valores nunca hubieran sido persistentes. Es posible que tenga que ver con el tipo de datos de la columna ( nvarchar(37)
) o posiblemente con el tamaño de la tabla (aproximadamente 7 millones de filas), pero SQL Server decidió ignorar elpersisted
palabra clave, al parecer, en este caso particular.
En este caso, la clave principal de la tabla es TransactionID, que también es una columna calculada y persistente. El plan de ejecución está generando un escaneo de índice y en una tabla con solo 7 millones de filas, esta consulta simple tarda más de 2-3 minutos en ejecutarse porque la función se ejecuta nuevamente en cada fila y los valores no parecen persistir en El índice.