Respostas:
Depende de como você define a coluna calculada. Uma PERSISTED
coluna computada será calculada e armazenada como dados dentro da tabela. Se você não definir a coluna como PERSISTED
, ela será calculada quando sua consulta for executada.
Por favor, veja a resposta de Aaron para uma ótima explicação e prova.
Pinal Dave também descreve isso em detalhes e mostra prova de armazenamento em sua série:
É muito fácil provar isso por conta própria. Podemos criar uma tabela com uma coluna computada que usa uma função escalar definida pelo usuário e, em seguida, verificar planos e estatísticas de funções antes e depois de uma atualização e seleção e ver quando uma execução é registrada.
Digamos que temos esta função:
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
E esta tabela:
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
Vamos verificar sys.dm_exec_function_stats
(novo no SQL Server 2016 e no Banco de Dados SQL do Azure) antes e depois de uma inserção e depois de uma seleção:
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();
Não vejo nenhuma chamada de função na inserção, apenas na seleção.
Agora, solte as tabelas e faça-o novamente, desta vez alterando a coluna para PERSISTED
:
DROP TABLE dbo.Floobs;
GO
DROP FUNCTION dbo.mask;
GO
...
MaskedName AS CONVERT(varchar(32), dbo.mask(Name)) PERSISTED,
...
E vejo o contrário acontecendo: recebo uma execução registrada na inserção, mas não na seleção.
Não tem uma versão moderna o suficiente do SQL Server para usar sys.dm_exec_function_stats
? Não se preocupe, isso também é capturado nos planos de execução .
Para a versão não persistente, podemos ver a função mencionada apenas no select:
Enquanto a versão persistente mostra apenas o cálculo acontecendo na inserção:
Agora, Martin menciona um grande ponto em um comentário : isso nem sempre será verdade. Vamos criar um índice que não cubra a coluna computada persistente, executar uma consulta que use esse índice e ver se a pesquisa obtém os dados dos dados persistentes existentes ou calcula os dados em tempo de execução (função de descartar e recriar e tabela aqui):
CREATE INDEX x ON dbo.Floobs(Name);
GO
INSERT dbo.Floobs(name)
SELECT LEFT(name, 32)
FROM sys.all_columns
WHERE LEN(name) >= 8;
Agora, executaremos uma consulta que usa o índice (na verdade, ele usa o índice por padrão nesse caso específico, mesmo sem a cláusula where):
SELECT * FROM dbo.Floobs WITH (INDEX(x))
WHERE Name LIKE 'S%';
Vejo execuções adicionais nas estatísticas das funções, e o plano não está:
Então, a resposta é DEPENDE . Nesse caso, o SQL Server achou que seria mais barato recalcular os valores do que realizar pesquisas. Isso pode mudar devido a uma variedade de fatores, por isso não confie nele. E isso pode acontecer em qualquer direção, independentemente de uma função definida pelo usuário ser usada ou não; Eu só usei aqui porque tornou muito mais fácil ilustrar.
A resposta para essa pergunta é realmente "depende". Acabei de encontrar um exemplo em que o SQL Server está usando o índice na coluna persistida computada, mas ainda está executando a função, como se os valores nunca tivessem persistido para começar. Pode ter a ver com o tipo de dados da coluna ( nvarchar(37)
) ou possivelmente com o tamanho da tabela (cerca de 7 milhões de linhas), mas o SQL Server decidiu ignorar a persisted
palavra-chave, ao que parece, nesta instância específica.
Nesse caso, a chave primária na tabela é TransactionID, que também é uma coluna calculada e persistida. O plano de execução está gerando uma varredura de índice e, em uma tabela com apenas 7 milhões de linhas, essa consulta simples leva mais de 2-3 minutos para ser executada, porque a função é executada novamente em todas as linhas e os valores não parecem persistir em o índice.