Die folgende Abfrage dauert ca. 10 Sekunden, bis eine Tabelle mit 12.000 Datensätzen abgeschlossen ist
select top (5) *
from "Physician"
where "id" = 1 or contains("lastName", '"a*"')
Aber wenn ich die where-Klausel in entweder ändere
where "id" = 1
oder
where contains("lastName", '"a*"')
Es wird sofort zurückkehren.
Beide Spalten sind indiziert und die Spalte lastName ist auch im Volltext indiziert.
CREATE TABLE Physician
(
id int identity NOT NULL,
firstName nvarchar(100) NOT NULL,
lastName nvarchar(100) NOT NULL
);
ALTER TABLE Physician
ADD CONSTRAINT Physician_PK
PRIMARY KEY CLUSTERED (id);
CREATE NONCLUSTERED INDEX Physician_IX2
ON Physician (firstName ASC);
CREATE NONCLUSTERED INDEX Physician_IX3
ON Physician (lastName ASC);
CREATE FULLTEXT INDEX
ON "Physician" ("firstName" LANGUAGE 0x0, "lastName" LANGUAGE 0x0)
KEY INDEX "Physician_PK"
ON "the_catalog"
WITH stoplist = off;
Hier ist der Ausführungsplan
Was könnte das Problem sein?
sql-server
sql-server-2008-r2
full-text-search
Hooman Valibeigi
quelle
quelle
Antworten:
Ihr Ausführungsplan
Wenn wir uns den Abfrageplan ansehen, sehen wir, dass ein Index berührt wird, um zwei Filteroperationen zu bedienen.
Ganz einfach ausgedrückt wurde aufgrund des TOP-Operators ein Zeilenziel festgelegt. Weitere Informationen und Voraussetzungen zu Reihenzielen finden Sie hier
Aus derselben Quelle:
Die gesamte Tabelle wird mithilfe eines linken Semi-Joins mit einem festgelegten Zeilenziel in den Filtern untersucht, in der Hoffnung, die 5 Zeilen so schnell und effizient wie möglich zurückzugeben.
Dies ist nicht der Fall, was zu vielen Iterationen über die .Fulltextmatch-TVF führt.
Erholung
Basierend auf Ihrem Plan konnte ich Ihr Problem etwas nachbauen:
Ausführen der Abfrage
Ergebnisse in einem Abfrageplan, der mit Ihrem vergleichbar ist:
Im obigen Beispiel ist B im Volltextindex nicht vorhanden. Infolgedessen hängt es von den Parametern und Daten ab, wie effizient der Abfrageplan sein kann.
Eine bessere Erklärung hierfür finden Sie in Row Goals, Part 2: Semi Joins von Paul White
Ändern Sie beispielsweise das Prädikat, damit die Ergebnisse viel früher gefunden werden (zu Beginn des Scans).
Das
where "id" = 124
wird eliminiert, da das Volltextindex-Prädikat bereits 5 Zeilen zurückgibt und dasTOP()
Prädikat erfüllt .Die Ergebnisse zeigen dies ebenfalls
Und die TVF-Hinrichtungen:
Neue Zeilen einfügen
Ausführen der Abfrage, um diese zuvor eingefügten Zeilen zu finden
Dies führt wiederum zu zu vielen Iterationen über fast alle Zeilen, um den vorletzten gefundenen Wert zurückzugeben.
Lösung
Beim Entfernen des Zeilenziels mithilfe des Traceflags 4138
Der Optimierer verwendet ein Verknüpfungsmuster, das der Implementierung von a näher
UNION
kommt. In unserem Fall ist dies günstig, da die Prädikate auf ihre jeweiligen Clustered-Index-Suchvorgänge verschoben werden und nicht der zeilengesteuerte linke Semi-Verknüpfungsoperator verwendet wird.Eine andere Möglichkeit, dies zu schreiben, ohne das oben erwähnte Traceflag zu verwenden:
Mit dem resultierenden Abfrageplan:
wobei die Volltextfunktion direkt angewendet wird
Als Randnotiz für op löste das Hotfix-Traceflag 4199 des Abfrageoptimierers sein Problem. Er implementierte dies, indem
OPTION(QUERYTRACEON(4199))
er der Abfrage hinzufügte . Ich konnte dieses Verhalten an meinem Ende nicht reproduzieren. Dieser Hotfix enthält eine Semi-Join-Optimierung:Quelle
Extra
Während der kostenbasierten Optimierung kann der Optimierer dem Ausführungsplan auch einen Indexspool hinzufügen, der von (oder dem physischen Gegenstück) implementiert wird.
LogOp_Spool Index on fly Eager
Dies geschieht mit meinem Datensatz für,
TOP(3)
aber nicht fürTOP(2)
Quelle
Mit dem Suchprädikat, das auf diese Index-Eiferspule angewendet wird:
quelle