Ho una colonna calcolata persistente su una tabella che è semplicemente composta da colonne concatenate, ad es
CREATE TABLE dbo.T
(
ID INT IDENTITY(1, 1) NOT NULL CONSTRAINT PK_T_ID PRIMARY KEY,
A VARCHAR(20) NOT NULL,
B VARCHAR(20) NOT NULL,
C VARCHAR(20) NOT NULL,
D DATE NULL,
E VARCHAR(20) NULL,
Comp AS A + '-' + B + '-' + C PERSISTED NOT NULL
);
In questo Comp
non è univoco e D è la data di inizio valida di ciascuna combinazione di A, B, C
, quindi utilizzo la seguente query per ottenere la data di fine per ciascuna A, B, C
(sostanzialmente la data di inizio successiva per lo stesso valore di Comp):
SELECT t1.ID,
t1.Comp,
t1.D,
D2 = ( SELECT TOP 1 t2.D
FROM dbo.T t2
WHERE t2.Comp = t1.Comp
AND t2.D > t1.D
ORDER BY t2.D
)
FROM dbo.T t1
WHERE t1.D IS NOT NULL -- DON'T CARE ABOUT INACTIVE RECORDS
ORDER BY t1.Comp;
Ho quindi aggiunto un indice alla colonna calcolata per aiutare in questa query (e anche in altri):
CREATE NONCLUSTERED INDEX IX_T_Comp_D ON dbo.T (Comp, D) WHERE D IS NOT NULL;
Il piano di query tuttavia mi ha sorpreso. Avrei pensato che da quando ho una clausola where che lo affermaD IS NOT NULL
e sto ordinando per Comp
, e non facendo riferimento a nessuna colonna esterna all'indice, l'indice sulla colonna calcolata potrebbe essere usato per scansionare t1 e t2, ma ho visto un indice cluster scansione.
Quindi ho costretto l'uso di questo indice per vedere se ha prodotto un piano migliore:
SELECT t1.ID,
t1.Comp,
t1.D,
D2 = ( SELECT TOP 1 t2.D
FROM dbo.T t2
WHERE t2.Comp = t1.Comp
AND t2.D > t1.D
ORDER BY t2.D
)
FROM dbo.T t1 WITH (INDEX (IX_T_Comp_D))
WHERE t1.D IS NOT NULL
ORDER BY t1.Comp;
Che ha dato questo piano
Ciò dimostra che viene utilizzata una ricerca chiave, i cui dettagli sono:
Ora, secondo la documentazione di SQL Server:
È possibile creare un indice su una colonna calcolata definita con un'espressione deterministica, ma imprecisa, se la colonna è contrassegnata come PERSISTED nell'istruzione CREATE TABLE o ALTER TABLE. Ciò significa che il Motore di database memorizza i valori calcolati nella tabella e li aggiorna quando vengono aggiornate eventuali altre colonne da cui dipende la colonna calcolata. Motore di database utilizza questi valori persistenti quando crea un indice sulla colonna e quando si fa riferimento all'indice in una query. Questa opzione consente di creare un indice su una colonna calcolata quando Motore di database non è in grado di dimostrare con precisione se una funzione che restituisce espressioni di colonne calcolate, in particolare una funzione CLR creata in .NET Framework, sia deterministica e precisa.
Quindi se, come dicono i documenti "Motore di database memorizza i valori calcolati nella tabella" e il valore viene anche memorizzato nel mio indice, perché è richiesta una Ricerca chiave per ottenere A, B e C quando non sono referenziati in la domanda a tutti? Presumo che vengano utilizzati per calcolare Comp, ma perché? Inoltre, perché la query può utilizzare l'indice su t2
, ma non su t1
?
NB Ho taggato SQL Server 2008 perché questa è la versione in cui si trova il mio problema principale, ma ho anche lo stesso comportamento nel 2012.