La siguiente consulta tarda unos 10 segundos en finalizar en una tabla con 12k registros
select top (5) *
from "Physician"
where "id" = 1 or contains("lastName", '"a*"')
Pero si cambio la cláusula where a cualquiera
where "id" = 1
o
where contains("lastName", '"a*"')
Volverá al instante.
Ambas columnas están indexadas y la columna lastName también está indexada en texto completo.
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;
Aquí está el plan de ejecución
¿Cual podría ser el problema?
sql-server
sql-server-2008-r2
full-text-search
Hooman Valibeigi
fuente
fuente
Respuestas:
Su plan de ejecucion
Al mirar el plan de consulta, podemos ver que se toca un índice para servir dos operaciones de filtro.
En pocas palabras, debido al operador TOP, se estableció un objetivo de fila. Puede encontrar mucha más información y requisitos previos sobre los objetivos de la fila aquí.
De esa misma fuente:
Toda la tabla se sondea en los filtros con el uso de una semiunión izquierda que tiene un objetivo de fila establecido, con la esperanza de devolver las 5 filas lo más rápido y eficiente posible.
Esto no sucede, lo que resulta en muchas iteraciones sobre .Fulltextmatch TVF.
Recreando
De acuerdo con su plan , pude recrear un poco su problema:
Ejecutando la consulta
Resultados en un plan de consulta comparable al suyo:
En el ejemplo anterior, B no existe en el índice de texto completo. Como resultado, depende del parámetro y los datos qué tan eficiente puede ser el plan de consulta.
Una mejor explicación de esto se puede encontrar en Row Goals, Parte 2: Semi Joins por Paul White
Por ejemplo, cambiar el predicado para que los resultados se encuentren mucho antes (al comienzo de la exploración).
el
where "id" = 124
es eliminado debido a la predicado índice de texto completo ya devolver 5 filas, satisfaciendo elTOP()
predicado.Los resultados muestran esto también
Y las ejecuciones de TVF:
Insertar algunas filas nuevas
Ejecutando la consulta para encontrar estas filas insertadas anteriormente
De nuevo, esto genera demasiadas iteraciones en casi todas las filas para devolver el último valor encontrado.
Resolviendo
Al eliminar el objetivo de la fila utilizando traceflag 4138
El optimizador utiliza un patrón de unión más cercano a la implementación de a
UNION
, en nuestro caso esto es favorable, ya que empuja los predicados hacia sus respectivas búsquedas de índice agrupado, y no usa el operador de semiunión izquierdo de la hilera.Otra forma de escribir esto, sin usar el indicador de rastreo mencionado anteriormente:
Con el plan de consulta resultante:
donde la función de texto completo se aplica directamente
Como nota al margen, para op, el hotfix del optimizador de consultas traceflag 4199 resolvió su problema. Lo implementó agregando
OPTION(QUERYTRACEON(4199))
a la consulta. No pude reproducir ese comportamiento de mi parte. Este hotfix contiene una optimización de semiunión:Fuente
Extra
Durante la optimización basada en costos, el optimizador también podría agregar una cola de índice al plan de ejecución, implementado por
LogOp_Spool Index on fly Eager
(o la contraparte física)Lo hace con mi conjunto de datos para
TOP(3)
pero no paraTOP(2)
Fuente
Con el predicado de búsqueda aplicado a este carrete ansioso de índice:
fuente