11

Tengo un proceso almacenado que busca productos (250,000 filas) usando un índice de texto completo.¿Por qué las interpretaciones de estas 2 consultas son tan diferentes?

El procedimiento almacenado tiene un parámetro que es la condición de búsqueda de texto completo. Este parámetro puede ser nulo, por lo que agregué una verificación nula y la consulta de repente comenzó a ejecutar órdenes de magnitud más lentas.

-- This is normally a parameter of my stored proc 
DECLARE @Filter VARCHAR(100) 
SET @Filter = 'FORMSOF(INFLECTIONAL, robe)' 

-- #1 - Runs < 1 sec 
SELECT TOP 100 ID FROM dbo.Products 
WHERE CONTAINS(Name, @Filter) 

-- #2 - Runs in 18 secs 
SELECT TOP 100 ID FROM dbo.Products 
WHERE @Filter IS NULL OR CONTAINS(Name, @Filter) 

Éstos son los planes de ejecución:

consulta # 1 Execution plant #1

consulta # 2 Execution plant #2

Debo admitir que no estoy muy familiarizado con los planes de ejecución. La única diferencia obvia para mí es que las uniones son diferentes. Intentaría agregar una pista, pero al no tener unirme a mi consulta, no estoy seguro de cómo hacerlo.

Asimismo, no entiendo muy bien por qué se utiliza el índice denominado IX_SectionID, ya que es un índice que sólo contiene la columna sectionid y que la columna no se utiliza en cualquier lugar.

Respuesta

8

OR puede aplastar el rendimiento, por lo que hacerlo de esta manera:

DECLARE @Filter VARCHAR(100) 
SET @Filter = 'FORMSOF(INFLECTIONAL, robe)' 

IF @Filter IS NOT NULL 
BEGIN 
    SELECT TOP 100 ID FROM dbo.Products 
    WHERE CONTAINS(Name, @Filter) 
END 
ELSE 
BEGIN 
    SELECT TOP 100 ID FROM dbo.Products 
END 

vistazo a este artículo: Dynamic Search Conditions in T-SQL by Erland Sommarskog y esta pregunta: SQL Server 2008 - Conditional Query.

+0

artículo de Niza - añadiendo opción '(RECOMPILE)' realmente resuelve el problema de rendimiento en la consulta segundo (sin embargo otro problema es que 'CONTIENE()' aumentos un error cuando el parámetro es NULL, pero ese es otro problema). –

1

Has introducido una condición OR. En la mayoría de los casos, es mucho más rápido comprobar explícitamente NULL y realizar una consulta en comparación con su método.

Por ejemplo intente esto:

IF @Filter IS NULL 
BEGIN 
SELECT TOP 100 ID FROM dbo.Products 
END 
ELSE 
BEGIN 
SELECT TOP 100 ID FROM dbo.Products 
WHERE @Filter CONTAINS(Name, @Filter) 
END 
3

El primer plan de consulta es sencillo:

  1. una búsqueda de texto completo para resolver CONTAINS(Name, @Filter)
  2. una exploración de índice para buscar las otras columnas de la filas encontradas
  3. combinan los dos usando una combinación hash

Los concatenation operator forma una unión de dos conjuntos de registros. Así que parece que la segunda consulta está haciendo:

  1. un recorrido de índice (más tarde utilizada para buscar otras columnas)
  2. una exploración constante. Supongo que está tratando su consulta como no parametrizada, por lo que el plan de consulta no tiene que funcionar para ningún otro valor de @Filter. Si es correcto, el escaneo constante resuelve @Filter is not null.
  3. una búsqueda de texto completo para resolver CONTAINS(Name, @Filter)
  4. sindicatos el resultado de 3 con el conjunto vacío de 2
  5. bucle se une el resultado de 1 y 4 para buscar las otras columnas

Una combinación hash intercambia memoria por velocidad; si su sistema tiene suficiente memoria, es mucho más rápido que una combinación de bucle. Esto puede explicar fácilmente una ralentización de 10-100x.

Una solución es utilizar dos consultas distintas:

if @Filter is null 
    SELECT TOP 100 ID FROM dbo.Products 
else 
    SELECT TOP 100 ID FROM dbo.Products WHERE CONTAINS(Name, @Filter) 
+0

Interesante: ¿hay alguna forma de forzar una combinación hash en la segunda consulta? –

+0

Sí, podría reescribirlo usando 'inner hash join', pero eso sería bastante complejo. Sería mejor usar una de las soluciones del artículo de Sommarskog. – Andomar

Cuestiones relacionadas