Quando vengono calcolate le colonne calcolate?


29

Quando vengono determinati i valori per le colonne calcolate?

  • Quando viene recuperato il valore?
  • Quando il valore viene modificato?
  • Un'altra volta?

Immagino che questa sia una domanda da principiante poiché non trovo nulla nelle mie ricerche.

Risposte:


19

Dipende da come si definisce la colonna calcolata. Una PERSISTEDcolonna calcolata verrà calcolata e quindi memorizzata come dati all'interno della tabella. Se non si definisce la colonna come PERSISTED, verrà calcolata quando viene eseguita la query.

Si prega di vedere la risposta di Aaron per una grande spiegazione e prova.

Pinal Dave lo descrive anche in dettaglio e mostra la prova di conservazione nella sua serie:

SQL SERVER - Colonna calcolata - PERSISTED e Archiviazione


6
Che dire se sono persistenti ma il piano di query utilizza un indice che non copre quella colonna? Non sono sicuro che otterresti una ricerca o se lo calcolerebbe al volo e non sia attualmente in grado di testarlo.
Martin Smith,

1
@Martin hai ragione, nel mio test SQL Server ha scelto il re-computing su una ricerca.
Aaron Bertrand

34

Questo è molto facile da provare da solo. È possibile creare una tabella con una colonna calcolata che utilizza una funzione scalare definita dall'utente, quindi controllare i piani e le statistiche delle funzioni prima e dopo sia un aggiornamento che una selezione e vedere quando viene registrata un'esecuzione.

Diciamo che abbiamo questa funzione:

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 questa tabella:

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

Controlliamo sys.dm_exec_function_stats(nuovo in SQL Server 2016 e nel database SQL di Azure) prima e dopo un inserimento, quindi dopo una selezione:

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

Non vedo alcuna chiamata di funzione sull'inserto, solo sulla selezione.

Ora lascia cadere le tabelle e fallo di nuovo, questa volta cambiando la colonna in PERSISTED:

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

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

E vedo accadere il contrario: ottengo un'esecuzione registrata sull'insert, ma non sulla selezione.

Non hai una versione abbastanza moderna di SQL Server da usare sys.dm_exec_function_stats? Nessun problema , anche questo viene catturato nei piani di esecuzione .

Per la versione non persistente, possiamo vedere la funzione indicata solo nella selezione:

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

Mentre la versione persistente mostra solo il calcolo in corso su insert:

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

Ora, Martin sottolinea un ottimo punto in un commento : questo non sarà sempre vero. Creiamo un indice che non copre la colonna calcolata persistente ed eseguiamo una query che utilizza tale indice e vediamo se la ricerca ottiene i dati dai dati persistenti esistenti o calcola i dati in fase di esecuzione (funzione drop and re-create e tabella qui):

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

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

Ora eseguiremo una query che utilizza l'indice (in realtà utilizza l'indice per impostazione predefinita in questo caso specifico, anche senza una clausola where):

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

Vedo ulteriori esecuzioni nelle statistiche delle funzioni e il piano non mente:

inserisci qui la descrizione dell'immagine

Quindi, la risposta è DIPENDE . In questo caso, SQL Server riteneva che sarebbe stato più economico ricalcolare i valori piuttosto che eseguire ricerche. Questo potrebbe cambiare a causa di una varietà di fattori, quindi non fare affidamento su di esso. E ciò può accadere in entrambe le direzioni indipendentemente dal fatto che venga utilizzata o meno una funzione definita dall'utente; L'ho usato solo qui perché lo ha reso molto più facile da illustrare.


Molto apprezzato, non ho mai messo in dubbio il comportamento del motore nei risultati di calcolo.
Arthur D,

8
@ArthurD È una decisione di ottimizzazione basata (principalmente) sui costi stimati di ciascuna alternativa, vedere la mia risposta a un'altra domanda qui.
Paul White dice GoFundMonica

-1

La risposta a questa domanda è davvero "dipende". Ho appena incontrato un esempio in cui SQL Server sta utilizzando l'indice sulla colonna calcolata persistente ma sta ancora eseguendo la funzione, come se i valori non fossero mai stati persistiti all'inizio. Potrebbe avere a che fare con il tipo di dati della colonna ( nvarchar(37)) o forse la dimensione della tabella (circa 7 milioni di righe), ma SQL Server ha deciso di ignorare la persistedparola chiave, a quanto pare, in questa particolare istanza.

In questo caso, la chiave primaria nella tabella è TransactionID che è anche una colonna calcolata e persistente. Il piano di esecuzione sta generando una scansione dell'indice e in una tabella con solo 7 milioni di righe questa semplice query impiega fino a 2-3 minuti per l'esecuzione perché la funzione viene eseguita nuovamente su ogni riga e i valori non sembrano persistere in l'indice.

creazione di tabella con colonna persistente è in corso l'esecuzione del piano di esecuzione che mostra la funzione

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.