Indice della colonna calcolata non utilizzato


14

Voglio avere una ricerca rapida basata su se due colonne sono uguali. Ho provato a utilizzare una colonna calcolata con un indice, ma SQL Server non sembra utilizzarla. Se utilizzo semplicemente una colonna di bit popolata staticamente con un indice, ottengo la ricerca dell'indice prevista.

Sembra che ci siano altre domande come questa là fuori, ma nessuna focalizzata sul perché un indice non verrebbe utilizzato.

Tabella di prova:

CREATE TABLE dbo.Diffs
    (
    Id int NOT NULL IDENTITY (1, 1),
    DataA int NULL,
    DataB int NULL,
    DiffPersisted  AS isnull(convert(bit, case when [DataA] is null and [DataB] is not null then 1 when [DataA] <> [DataB] then 1 else 0 end), 0) PERSISTED ,
    DiffComp  AS isnull(convert(bit, case when [DataA] is null and [DataB] is not null then 1 when [DataA] <> [DataB] then 1 else 0 end), 0),
    DiffStatic bit not null,
    Primary Key (Id)
    )

create index ix_DiffPersisted on Diffs (DiffPersisted)
create index ix_DiffComp on Diffs (DiffComp)
create index ix_DiffStatic on Diffs (DiffStatic)

E la query:

select Id from Diffs where DiffPersisted = 1
select Id from Diffs where DiffComp = 1
select Id from Diffs where DiffStatic = 1

E i piani di esecuzione risultanti: Progetto esecutivo

Risposte:


10

Prova con COALESCEinvece di ISNULL. Con ISNULL, SQL Server non sembra in grado di spingere un predicato contro l'indice più stretto e quindi deve scansionare il cluster per trovare le informazioni.

CREATE TABLE dbo.Diffs
    (
    Id int NOT NULL IDENTITY (1, 1),
    DataA int NULL,
    DataB int NULL,
    DiffPersisted  AS COALESCE(convert(bit, case when [DataA] is null 
      and [DataB] is not null then 1 when [DataA] <> [DataB] 
      then 1 else 0 end), 0) PERSISTED ,
    DiffComp  AS COALESCE(convert(bit, case when [DataA] is null 
      and [DataB] is not null then 1 when [DataA] <> [DataB] 
      then 1 else 0 end), 0),
    DiffStatic bit not null,
    Primary Key (Id)
    );

Detto questo, se rimani con una colonna statica, un indice filtrato potrebbe avere più senso e avrà costi di I / O più bassi (tutto a seconda di quante righe corrispondono in genere al predicato del filtro), ad esempio:

CREATE INDEX ix_DiffStaticFiltered 
  ON dbo.Diffs(DiffStatic)
  WHERE DiffStatic = 1;

Molto interessante, non ci avrei pensato. Sembra che COALESCEa questo punto puoi semplicemente sbarazzartene ; Credo che la CASEdichiarazione fosse già garantita per la restituzione 0o 1, ma ISNULLera presente solo in modo che SQL Server avrebbe prodotto un valore nulla BITper la colonna calcolata. Tuttavia, COALESCEprodurrà comunque una colonna nullable. Quindi l'unico impatto di questa modifica, con o senza il COALESCE, è che la colonna calcolata è ora nullable ma la ricerca dell'indice può essere utilizzata.
Geoff Patterson,

@Geoff Sì, è vero. Ma in questo caso, poiché sappiamo che dalla definizione di colonna calcolata NULL non è davvero un output possibile, questo conta davvero solo se stiamo usando questa tabella come sorgente di un SELECT INTO.
Aaron Bertrand

Queste sono alcune informazioni straordinarie - grazie! Il mio obiettivo finale è che le colonne DataA e DataB vengano utilizzate come uuidi "sporchi" per consentire l'aggiornamento asincrono delle colonne denormalizzate nel record, quindi non dovrebbero esserci troppe posizioni in cui il flag Diff è 1. Se uso la statica campo, quindi stavo pensando di aggiungere un trigger per monitorare i due uuidi e aggiornare il campo.
David Faivre,

Inoltre, come ha sottolineato @GeoffPatterson, non posso usare COALESCE? Perché dovrei tenerlo?
David Faivre,

@David Probabilmente puoi rilasciare il file COALESCE. Ho cercato di mantenere l'aspetto e l'intento del tuo codice originale e non ho testato senza di esso, in modo che il test sarà su di te. (Non riesco nemmeno a spiegare perché tu abbia avuto ISNULLlì, in primo luogo.)
Aaron Bertrand

5

Questa è una limitazione specifica della logica di corrispondenza della colonna calcolata di SQL Server, quando ISNULLviene utilizzata una più esterna , e il tipo di dati della colonna è bit.

Riportare un errore

Per evitare il problema, è possibile utilizzare una delle soluzioni alternative seguenti:

  1. Non utilizzare un valore esterno ISNULL(l'unico modo per creare una colonna calcolata NOT NULL).
  2. Non utilizzare il bit tipo di dati come tipo finale della colonna calcolata.
  3. Creare la colonna calcolata PERSISTEDe abilitare il flag di traccia 174 .

Dettagli

Il cuore del problema è che senza il flag di traccia 174, tutti i riferimenti di colonna calcolati in una query (anche persistenti) vengono sempre espansi nella definizione sottostante molto presto nella compilazione della query.

L'idea di espansione è che potrebbe consentire semplificazioni e riscritture che possono funzionare solo sulla definizione, non solo sul nome della colonna. Ad esempio, potrebbero esserci predicati nella query che fanno riferimento a quella colonna calcolata che potrebbero rendere ridondante parte del calcolo o comunque più vincolata.

Una volta prese in considerazione le semplificazioni e le riscritture iniziali, la compilazione delle query tenta di far corrispondere le espressioni della query alle colonne calcolate (tutte le colonne calcolate, non solo quelle originariamente trovate nel testo della query).

Le espressioni di colonna calcolate invariate corrispondono alla colonna calcolata originale senza problemi nella maggior parte dei casi. Sembra esserci un bug quando specifico per abbinare un'espressione di bittipo, con una più esterna ISNULL. La corrispondenza non ha esito positivo in questo caso specifico, anche se un esame dettagliato degli interni mostra che dovrebbe avere successo.

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.