Berechneter Spaltenindex wird nicht verwendet

14

Ich möchte eine schnelle Suche basierend auf, wenn zwei Spalten gleich sind. Ich habe versucht, eine berechnete Spalte mit einem Index zu verwenden, aber SQL Server scheint ihn nicht zu verwenden. Wenn ich nur eine statisch aufgefüllte Bitspalte mit einem Index verwende, erhalte ich die erwartete Indexsuche.

Es scheint, als gäbe es einige andere Fragen wie diese, aber keine konzentrierte sich darauf, warum ein Index nicht verwendet werden würde.

Testtabelle:

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)

Und die Abfrage:

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

Und die daraus resultierenden Ausführungspläne: Ausführungsplan

David Faivre
quelle

Antworten:

10

Versuchen Sie es mit COALESCEanstelle von ISNULL. Mit ISNULLscheint SQL Server nicht in der Lage zu sein, ein Prädikat gegen den engeren Index zu drücken, und muss daher den Cluster durchsuchen, um die Informationen zu finden.

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

Wenn Sie sich jedoch an eine statische Spalte halten, ist ein gefilterter Index möglicherweise sinnvoller und verursacht geringere E / A-Kosten (je nachdem, wie viele Zeilen normalerweise mit dem Filterprädikat übereinstimmen). Beispiel:

CREATE INDEX ix_DiffStaticFiltered 
  ON dbo.Diffs(DiffStatic)
  WHERE DiffStatic = 1;
Aaron Bertrand
quelle
Sehr interessant, hätte nicht gedacht. Es scheint so, als ob Sie das COALESCEan dieser Stelle einfach loswerden können . Ich glaube, die CASEAnweisung wurde bereits garantiert, um zurückzugeben, 0oder 1, aber ISNULLwar nur vorhanden, damit SQL Server ein nicht nullable BITfür die berechnete Spalte ergeben würde. Es wird jedoch COALESCEimmer noch eine nullfähige Spalte angezeigt. Die einzige Auswirkung dieser Änderung mit oder ohne COALESCEist, dass die berechnete Spalte jetzt nullwertfähig ist, aber die Indexsuche verwendet werden kann.
Geoff Patterson
@Geoff Ja, das stimmt. Aber in diesem Fall , da wir durch die berechnete Spaltendefinition NULL wissen , ist wirklich nicht eine mögliche Ausgabe, das nur wirklich wichtig ist, wenn wir diese Tabelle als Quelle eines SELECT INTO verwenden.
Aaron Bertrand
Dies sind einige erstaunliche Informationen - danke! Mein Endziel ist es, dass die DataA- und DataB-Spalten als "schmutzige" UUIDs verwendet werden, um eine asynchrone Aktualisierung denormalisierter Spalten im Datensatz zu ermöglichen. Wenn ich die statische Variable verwende, sollten nicht zu viele Spalten vorhanden sein field, dann dachte ich darüber nach, einen Trigger hinzuzufügen, um die beiden uuids zu überwachen und das field zu aktualisieren.
David Faivre
Kann ich auch, wie @GeoffPatterson betonte, das nicht verwenden COALESCE? Warum sollte ich es behalten?
David Faivre
@David Du kannst das wohl fallen lassen COALESCE. Ich habe versucht, das Erscheinungsbild und die Absicht Ihres ursprünglichen Codes beizubehalten, und habe nicht ohne diesen Code getestet, damit Sie die Tests durchführen können. (Ich kann auch nicht erklären, warum Sie ISNULLdort waren.)
Aaron Bertrand
5

Dies ist eine spezielle Einschränkung der berechneten SQL Server-Spaltenübereinstimmungslogik, wenn eine äußerste ISNULLverwendet wird und der Datentyp der Spalte lautet bit.

Fehlerbericht

Um das Problem zu vermeiden, kann eine der folgenden Problemumgehungen angewendet werden:

  1. Verwenden Sie keine äußerste ISNULLSpalte (die einzige Möglichkeit, eine berechnete Spalte zu erstellen NOT NULL).
  2. Verwenden Sie den bitDatentyp nicht als endgültigen Typ der berechneten Spalte.
  3. Erstellen Sie die berechnete Spalte PERSISTEDund aktivieren Sie das Ablaufverfolgungsflag 174 .

Einzelheiten

Der Kern des Problems besteht darin, dass ohne Ablaufverfolgungsflag 174 alle berechneten Spaltenreferenzen in einer Abfrage (auch wenn sie bestehen bleiben) immer sehr früh in der zugrunde liegenden Definition bei der Abfragekompilierung erweitert werden.

Die Idee der Erweiterung besteht darin, Vereinfachungen und Umschreibungen zu ermöglichen, die nur für die Definition gelten, nicht nur für den Spaltennamen. Beispielsweise kann die Abfrage Prädikate enthalten, die auf diese berechnete Spalte verweisen und einen Teil der Berechnung überflüssig oder auf andere Weise eingeschränkt machen.

Sobald frühe Vereinfachungen und Umschreibungen berücksichtigt wurden, versucht die Abfragekompilierung, Ausdrücke in der Abfrage mit berechneten Spalten abzugleichen (alle berechneten Spalten, nicht nur diejenigen, die ursprünglich im Abfragetext enthalten waren).

Unveränderte berechnete Spaltenausdrücke stimmen in den meisten Fällen problemlos mit der ursprünglichen berechneten Spalte überein. Es scheint einen Fehler zu geben, wenn ein Ausdruck vom bitTyp mit einem äußersten übereinstimmt ISNULL. Der Abgleich ist in diesem speziellen Fall nicht erfolgreich, auch wenn eine detaillierte Prüfung der Interna ergibt, dass der Abgleich erfolgreich sein sollte.

Paul White Monica wieder einsetzen
quelle