Quando as colunas computadas são calculadas?

29

Quando são determinados os valores para as colunas computadas?

  • Quando o valor é recuperado?
  • Quando o valor é alterado?
  • Em alguma outra hora?

Acho que essa é uma pergunta iniciante, pois não encontro nada nas minhas pesquisas.

Shelby115
fonte

Respostas:

19

Depende de como você define a coluna calculada. Uma PERSISTEDcoluna 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:

SQL SERVER - Coluna Computada - PERSISTIDA e Armazenamento

Arthur D
fonte
6
E se eles persistirem, mas o plano de consulta usar um índice que não cobre essa coluna? Não tenho certeza se você faria uma pesquisa ou se a computaria imediatamente e não pode testá-la no momento.
Martin Smith
1
@ Martin, você está certo. No meu teste, o SQL Server escolheu a re-computação em vez de uma pesquisa.
Aaron Bertrand
34

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

insira a descrição da imagem aqui

insira a descrição da imagem aqui

Enquanto a versão persistente mostra apenas o cálculo acontecendo na inserção:

insira a descrição da imagem aqui

insira a descrição da imagem aqui

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

insira a descrição da imagem aqui

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.

Aaron Bertrand
fonte
Muito apreciado, nunca questionei o comportamento do mecanismo nos resultados da computação.
Arthur D
8
@ArthurD É uma decisão do otimizador baseada (principalmente) nos custos estimados de cada alternativa, veja minha resposta para outra pergunta aqui.
Paul White diz GoFundMonica
-1

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

criando tabela com coluna persistente plano de execução mostrando a função está sendo executada

Brony corpulento
fonte