La requête suivante prend environ 10 secondes pour terminer sur une table avec 12k enregistrements
select top (5) *
from "Physician"
where "id" = 1 or contains("lastName", '"a*"')
Mais si je change la clause where en
where "id" = 1
ou
where contains("lastName", '"a*"')
Il reviendra instantanément.
Les deux colonnes sont indexées et la colonne lastName est également indexée en texte intégral.
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;
Voici le plan d'exécution
Quel pourrait être le problème?
sql-server
sql-server-2008-r2
full-text-search
Hooman Valibeigi
la source
la source
Réponses:
Votre plan d'exécution
En regardant le plan de requête, nous pouvons voir qu'un index est touché pour servir deux opérations de filtrage.
En termes très simples, en raison de l'opérateur TOP, un objectif de ligne a été fixé. De plus amples informations et conditions préalables sur les objectifs de rang peuvent être trouvées ici
De cette même source:
La table entière est sondée dans les filtres à l'aide d'une semi-jointure gauche qui a un objectif de ligne défini, dans l'espoir de renvoyer les 5 lignes aussi rapidement et efficacement que possible.
Cela ne se produit pas, ce qui entraîne de nombreuses itérations sur le .Fulltextmatch TVF.
Recréer
Sur la base de votre plan , j'ai pu recréer quelque peu votre problème:
Exécution de la requête
Résultats dans un plan de requête comparable au vôtre:
Dans l'exemple ci-dessus, B n'existe pas dans l'index de texte intégral. En conséquence, cela dépend du paramètre et des données de l'efficacité du plan de requête.
Une meilleure explication de cela peut être trouvée dans Row Goals, Part 2: Semi Joins par Paul White
Par exemple, changer le prédicat pour que les résultats soient trouvés bien plus tôt (au début de l'analyse).
le
where "id" = 124
est éliminé en raison du prédicat d'index de texte intégral renvoyant déjà 5 lignes, satisfaisant leTOP()
prédicat.Les résultats le montrent également
Et les exécutions de TVF:
Insertion de nouvelles lignes
Exécution de la requête pour rechercher ces lignes insérées précédentes
Cela entraîne à nouveau trop d'itérations sur presque toutes les lignes pour renvoyer la dernière valeur trouvée.
Résolution
Lors de la suppression de l'objectif de ligne à l'aide de traceflag 4138
L'optimiseur utilise un modèle de jointure plus proche de l'implémentation de a
UNION
, dans notre cas, cela est favorable car il pousse les prédicats vers le bas vers leurs recherches d'index cluster respectives, et n'utilise pas l'opérateur de demi-jointure gauche ciblé par ligne.Une autre façon d'écrire ceci, sans utiliser le traceflag mentionné ci-dessus:
Avec le plan de requête résultant:
où la fonction de texte intégral est appliquée directement
En guise de note, pour op, le correctif de l'optimiseur de requêtes traceflag 4199 a résolu son problème. Il a implémenté cela en ajoutant
OPTION(QUERYTRACEON(4199))
à la requête. Je n'ai pas pu reproduire ce comportement de mon côté. Ce correctif contient une optimisation de semi-jointure:La source
Supplémentaire
Pendant l'optimisation basée sur les coûts, l'optimiseur peut également ajouter une bobine d'index au plan d'exécution, implémentée par
LogOp_Spool Index on fly Eager
(ou l'homologue physique)Il le fait avec mon jeu de données pour
TOP(3)
mais pas pourTOP(2)
La source
Avec le prédicat de recherche appliqué à cette bobine impatiente d'index:
la source