2009-10-14 8 views
9

Tengo una tabla con aproximadamente 117000 registros. Necesito realizar una búsqueda que compruebe 3 campos separados para un patrón de cadena dado.¿Cómo puedo optimizar/refactorizar una cláusula TSQL "ME GUSTA"?

Mi cláusula where es el siguiente:

field1 LIKE '%' + @DESC + '%' 
OR field2 LIKE '%' + @DESC + '%' 
OR field3 LIKE '%' + @DESC + '%' 

Esto parece tomar aproximadamente 24 segundos, sean de entrada ...

¿Hay una mejor manera de hacer esto? Menos de 10 (¡o 5!) Segundos serían mucho más preferibles.

Gracias por cualquier ayuda.

Respuesta

14

Utilice la búsqueda de texto completo y CONTAINS. LIKE no se puede optimizar al buscar en el medio del campo, es decir. cuando la expresión LIKE comienza con un '%', por lo que siempre hará un escaneo completo de la tabla.

+0

puedes use un índice, con un poco de trabajo, cuando haga LIKE '%' + cadena, vea el enlace en mi otro comentario. –

+0

@KM: truco interesante, una columna invertida. También una buena analogía con la guía telefónica, hace que el caso sea vívidamente claro. –

+0

buen truco, obad no hay nada similar para el problema '% text%'. – HLGEM

1

Cada vez que inicia una búsqueda LIKE con un comodín, está haciendo un escaneo. A menos que pueda restringir sus criterios de búsqueda para incluir el primer carácter (que puede no ser factible), deberá recurrir a la Búsqueda de texto completo.

+3

@ Stuart Ainsworth dijo: "Siempre que inicie una búsqueda LIKE con un comodín, está haciendo un scan_, que no tiene que ser cierto, vea esto: http://stackoverflow.com/questions/1388059/sql-server-index -columns-used-in-like/1395881 # 1395881 –

+0

Ahora solo juegas con semántica; REVERSE la columna e indexe, y luego invierta la cláusula similar para que no comience con un comodín. No está comenzando una búsqueda con un comodín, por lo tanto, no está haciendo un escaneo. Debo admitir que es una solución interesante, y tendré que tenerla en cuenta. –

0

¿qué tal

field1 + field2 + field3 LIKE '%' + @DESC + '%' 

o

CONTAINS(field1 + field2 + field3, @DESC) 
1

¿Usted realmente necesita para comenzar con un comodín? ¿Por qué? A menudo puede obligar a los usuarios a escribir al menos el primer carácter. Traigo esto a colación porque algunos desarrolladores solo usan el comodín como un hábito, no porque exista un requisito. En la mayoría de los casos, los usuarios podrán escribir el primer carácter a menos que el archivo guarde cadenas largas (como, por ejemplo, nombres de aeropuertos oficiales). De lo contrario, realmente necesita utilizar la indexación de texto completo, aunque el truco de KM con el revés es bastante bueno si no necesita el comodín al final.

Si puede evitar que el rendimiento mate cosas, hágalo.

+0

Sí, necesito un comodín de doble cara para esta consulta. Necesito encontrar algo como 'banana' en 'Strawberry Banana Yogurt'. – IronicMuffin

1

Aunque estoy de acuerdo con la respuesta aceptada de que la indización de texto completo sería la mejor solución y soy de ninguna manera que abogan por el uso de los principales comodines si tienen que realizarse entonces no son posibles pasos que se pueden tomar para hacer que el rendimiento de ellos sea menos malo.

Kalen Delaney en el libro "Microsoft SQL Server 2008 Internals" dice:

intercalación puede hacer una gran diferencia cuando SQL Server tiene que mirar casi todos los caracteres de las cadenas. Para ejemplo, mira el siguiente:

SELECT COUNT(*) FROM tbl WHERE longcol LIKE '%abc%' 

Esto puede ejecutar 10 veces más rápido o más con una intercalación binaria de una colación no binario de Windows. Y con los datos varchar, esto se ejecuta hasta siete u ocho veces más rápido con una intercalación de SQL que con una intercalación de Windows.

+0

¿Cómo afecta el cotejo al rendimiento? – crosenblum

+0

Más del libro "Si tiene una columna varchar, puede acelerar esto forzando la intercalación de la siguiente manera: SELECT COUNT (*) FROM tbl DONDE longcol COLLATE SQL_Latin1_General_CP_CI_AS LIKE '% abc%';" –

+0

@yoelhalb probablemente significó '_CP1_' no' _CP_' – jazzcat

0

Intenté una posible solución. Antes de esta solución, incluso la consulta no devolvía el resultado y causaba un error de tiempo de espera de conexión.

Mi consulta tenía un filtro de fecha y otros criterios.Todos los otros criterios fueron como búsqueda. Una palabra clave de columna estaba buscando como '% abc%' en la columna ntext y estaba haciendo un escaneo completo de la tabla.

Solución:

Dividir consulta en 2 partes. 1) Primera parte en CTE (Common Table Express) 2) Aplicar todos los criterios de búsqueda en CTE.

WITH SearchData(Column1,Column2,Column3,Column4,........) 
    AS 
    (
    SELECT Column1,Column2,Column3,Column4,........... 
    FROM myTable1 WITH(NOLOCK) 
      INNER JOIN MyTable2 WITH(NOLOCK) 
       ON MyTable1.id = MyTable2.Id 
    WHERE (MyTable1.CreationTime >= '2014-04-27' AND MyTable1.CreationTime <= '2014-05-01') 
) 

    SELECT DISTINCT top 250 Column1,Column2,Column3,Column4 
    FROM SearchData 
    WHERE (ISNULL(Column1,'') LIKE @Column1 +'%' OR @Column1 IS NULL) 
      and (Column2 LIKE @Column2+ '%' OR @Column2 IS NULL) 
      ... 
      ... 
      ... 
      ... 
      AND (Column10 like '%'[email protected]+'%' or @Column10 IS NULL) 
      AND @[email protected][email protected][email protected] <> '' 
      ORDER BY [CreationTime] DESC 

Me funcionó.

+0

Creo que el uso de la cláusula 'TOP' con una consulta que tiene una condición LIKE siempre mejorará el rendimiento. Así que es la cláusula TOP en lugar de un CTE que contribuye a la ganancia de rendimiento, ya que con TOP un escaneo completo se interrumpe tan pronto como se escanea el número requerido de registros. – Sunil

+1

como una nota fuera del tema que usa WITH (NOLOCK) en cada unión es muy peligroso, debes ser muy cuidadoso con ella o obtendrás datos duplicados, etc. ... Veo gente que usa esta sugerencia como una forma estándar de optimizar consultas, es no.siempre debe pensar dos veces y revisar la forma en que se escriben las tablas (insertadas/actualizadas) en su aplicación antes de manipular una pista de nolock. (solo digo) –

0

Si no puede usar FullTextSearch, puede aumentar la velocidad en 10 veces. Hacer a continuación:

1 Agregar campo calculado: Índice

alter table TableName 
add CalculatedColumnName as upper(Column1 + '|' + Column2...) collate Latin1_General_100_Bin2 
persisted; 

2 Agregar para el campo calculado:

create nonclustered index IDX_TableName_CalculatedColumnName 
on TableName(CalculatedColumnName); 

3 Cambiar el texto de la consulta

select count(*) 
from TableName 
where CalculatedColumnName like '%' + upper(@ParameterValue) + '%' collate Latin1_General_100_Bin2 

Fuente: http://aboutsqlserver.com/2015/01/20/optimizing-substring-search-performance-in-sql-server

Cuestiones relacionadas