2009-08-18 14 views
8

Tengo una tabla con aproximadamente 1 millón de registros (ejecutando SQL Server 2008 Web). Tengo una rutina de búsqueda que intenta hacer coincidir el código del producto y la descripción del producto. Sin embargo, en algunas circunstancias es muy lento. A continuación se muestra (corte hacia abajo) instrucción SQL:¿Por qué esta declaración de SQL es muy lenta?

WITH AllProducts AS (
    SELECT  p.*, Row_Number() OVER (ORDER BY ProductId) AS RowNumber 
    FROM  Product AS p 
    WHERE p.IsEnabled=1 AND 
    (
     p.BaseSku = 'KPK-3020QWC-C' -- this on its own is fast 
     OR 
     CONTAINS(p.FreeTextStrings, '"KPK-3020QWC*"') -- and this on its own is fast, but not both 
    ) 
) SELECT * FROM AllProducts   
    WHERE RowNumber BETWEEN 1 AND 20; 

Tenga en cuenta que si sólo se compara en [p.BaseSku = 'KPK-3020QWC-C'] o [CONTIENE (p.FreeTextStrings '", KPK-3020QWC * "')] individualmente (pero no ambos) es instantáneo. Y si los comparo juntos, lleva años (varios minutos) y devuelve solo una fila.

IsEnabled y BaseSku están indexados, y FreeTextStrings está indexado en FTS.

Recuerdo que esto funcionaba bien antes.

¿Alguien puede arrojar algo de luz sobre esto y sugerir algunas soluciones?

archivo de plan de ejecución está disponible aquí: http://wiki.webgear.co.nz/GetFile.aspx?File=Temp%5cSearch%20Test.sqlplan.zip

+0

¿Puede mostrarnos un plan de ejecución de su servidor SQL? –

+0

Estos problemas comenzaron a ocurrir después de actualizar SQL 2005 a SQL 2008. – Muxa

Respuesta

10

or es notoriamente lento en SQL Server. Es irritante, por decir lo menos.

Intenta dividirlo en dos consultas con un union:

WITH AllProducts AS (
    select *, Row_Number() OVER (ORDER BY ProductId) AS RowNumber 
    from (
    SELECT  p.* 
    FROM  Product AS p 
    WHERE p.IsEnabled=1 AND 
     p.BaseSku = 'KPK-3020QWC-C' 
    UNION 
    SELECT  p.* 
    FROM  Product AS p 
    WHERE p.IsEnabled=1 AND 
     CONTAINS(p.FreeTextStrings, '"KPK-3020QWC*"') 
) 
) SELECT * FROM AllProducts   
    WHERE RowNumber BETWEEN 1 AND 20; 
+1

Exactamente, es probable que el "o" provoque un escaneo de tabla en la tabla de producto, incluso si hay índices en BaseSku y FreeTextStrings ... Una unión cambiará eso para indexar el análisis de búsqueda + índice ... (suponiendo que haya índices que cubran esas dos columnas) – KristoferA

+0

Lo he intentado y de hecho ha mostrado grandes mejoras.Probaré esta técnica con la declaración sql completa. – Muxa

+0

Sí, esto lo ha resuelto. Sin embargo, creo que es bastante extraño que la misma declaración funcionó muy rápido en SQL 2005 y está trabajando lento en SQL 2008. ¿Puede ser que esto se aborde en el siguiente service pack? – Muxa

1

Esto parece funcionar bien:

WITH AllProducts AS (
    SELECT  p.*, Row_Number() OVER (ORDER BY ProductId) AS RowNumber 
    FROM  Product AS p 
    WHERE p.IsEnabled=1 AND 
    (
     CONTAINS(p.BaseSku, 'KPK-3020QWC-C') /* instead of p.BaseSku = 'KPK-3020QWC-C' */ 
     OR 
     CONTAINS(p.FreeTextStrings, '"KPK-3020QWC*"') 
    ) 
) SELECT * FROM AllProducts   
    WHERE RowNumber BETWEEN 1 AND 20; 

(que ya tenía BaseSku FTS indexado-)

0

Make sure all necessary indexes are in place. I tuve el mismo problema con una cláusula or en una de mis consultas y la creación de un ÍNDICE NO CULPADO con columnas INCLUDE corrigió el rendimiento .

Después de más pruebas, fue el columna INCLUDE parte del índice que realmente fija el problema de rendimiento. Esto es lo que hice para determinar el problema y cómo solucionarlo:

utilizar el plan de ejecución para ayudarle a crear los índices faltantes:

sin el índice de la consulta estaba tomando 2+ min cuando debería tener funcionó en unos pocos milisegundos. Así que comparé los planes de ejecución de la consulta con y sin la cláusula or en SSMS y no era obvio lo que tenía que hacer (sobre todo debido a mi falta de comprensión de los planes de ejecución).

Pero si mira por encima del plan de ejecución en texto verde, SSMS puede indicarle que cree un índice no agrupado. Hmm ... vale la pena intentarlo. ¡Así que creé el índice y el problema resuelto! Puede hacer clic con el botón derecho en la consulta "CREAR ÍNDICE" y seleccionar "Detalles de índice faltantes ...". Esto abrirá una nueva pestaña con la consulta completa para que pueda ejecutar. Solo dale un nombre.