2010-09-27 17 views
5

Tengo una columna EntityName, y quiero que los usuarios puedan buscar nombres ingresando palabras separadas por espacio. El espacio se considera implícitamente como un operador 'Y', lo que significa que las filas devueltas deben tener todas las palabras especificadas, y no necesariamente en el orden dado.SQL: Encuentre filas donde Column contiene todas las palabras dadas

Por ejemplo, si tenemos filas como éstas:

  1. Abba nina bonita balerina
  2. acdc me sacudió toda la noche
  3. algo que me está
  4. sueño teatro que es todo acerca de ti

cuando el usuario ingresa: me you, o you me (los resultados deben ser equivalentes), el resultado tiene filas 2 y 3.

sé que puedo ir como:

WHERE Col1 LIKE '%' + word1 + '%' 
    AND Col1 LIKE '%' + word2 + '%' 

pero quería saber si hay alguna solución más óptima.

El CONTAINS requerirá un índice de texto completo, que (por diversas razones) no es una opción.

¿Quizás Sql2008 tiene alguna solución incorporada, semi oculta para estos casos?

+1

Tengo curiosidad de saber las razones por la solución índice de texto completo está fuera de la mesa . Esa es ciertamente la forma en que me gustaría ir aquí. –

+0

Perdón por una respuesta tardía: no nos funciona porque no admite búsquedas como la que utilicé como ejemplo en cuestión ('% término%'), la búsqueda que no está limitada a palabras separadas, sino incluso palabras que solo contener el término en él). Y, además, SqlServer está en una máquina agrupada con unidades de red compartidas, y cualquier instalación adicional está congelada (y debemos instalar la búsqueda de texto completo, porque los administradores no la incluyeron al instalarla): nos aseguran que es un infierno hacer instalaciones adicionales a los nodos ... así que es por eso que está fuera de la mesa ... – veljkoz

Respuesta

3

Lo único que se me ocurre es escribir una función CLR que haga las comparaciones LIKE. Esto debería ser muchas veces más rápido.

Actualización: Ahora que lo pienso, tiene sentido que CLR no ayude. Otros dos ideas:

1 - Pruebe la indexación de Col1 y hacer esto:

WHERE (Col1 LIKE word1 + '%' or Col1 LIKE '%' + word1 + '%') 
    AND (Col1 LIKE word2 + '%' or Col1 LIKE '%' + word2 + '%') 

Dependiendo de las búsquedas más comunes (comienza con la subcadena vs), esto puede ofrecer una mejora.

2 - Agregue su propia tabla de indización de texto completo donde cada palabra es una fila en la tabla. Entonces puedes indexar correctamente.

+0

Aunque al principio estaba en contra, parece que es la mejor solución hasta ahora ... – veljkoz

+0

Después de que lo intenté, quiero para agregar una actualización a esto - es increíblemente lento ... si el método 'me gusta' termina en 10 segundos, esta función de CLR necesita ... bueno, no sé - acabo de detenerlo después de 20 minutos ... esta solución también se archiva ... – veljkoz

+0

Publique su código ... – RedFilter

2

usted va a terminar con una mesa de exploración completa de todos modos.

La intercalación puede hacer una gran diferencia aparentemente. 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

Este es un buen punto, pero ya hemos establecido colaciones apropiadamente ... – veljkoz

1
WITH Tokens AS(SELECT 'you' AS Token UNION ALL SELECT 'me') 
SELECT ... 
FROM YourTable AS t 
WHERE (SELECT COUNT(*) FROM Tokens WHERE y.Col1 LIKE '%'+Tokens.Token+'%') 
= 
(SELECT COUNT(*) FROM Tokens) ; 
+0

Un enfoque interesante, pero lamentablemente muy lento ... – veljkoz

3

Función

CREATE FUNCTION [dbo].[fnSplit] (@sep CHAR(1), @str VARCHAR(512)) 
RETURNS TABLE AS 
RETURN (
      WITH Pieces(pn, start, stop) AS (
      SELECT 1, 1, CHARINDEX(@sep, @str) 
      UNION ALL 
      SELECT pn + 1, stop + 1, CHARINDEX(@sep, @str, stop + 1) 
      FROM Pieces 
      WHERE stop > 0 
    ) 

     SELECT 
      pn AS Id, 
      SUBSTRING(@str, start, CASE WHEN stop > 0 THEN stop - start ELSE 512 END) AS Data 
     FROM 
      Pieces 
) 

consulta

DECLARE @FilterTable TABLE (Data VARCHAR(512)) 

INSERT INTO @FilterTable (Data) 
SELECT DISTINCT S.Data 
FROM fnSplit(' ', 'word1 word2 word3') S -- Contains words 

SELECT DISTINCT 
     T.* 
FROM 
     MyTable T 
     INNER JOIN @FilterTable F1 ON T.Col1 LIKE '%' + F1.Data + '%' 
     LEFT JOIN @FilterTable F2 ON T.Col1 NOT LIKE '%' + F2.Data + '%' 
WHERE 
     F2.Data IS NULL 

Fuente: SQL SELECT WHERE field contains words

Cuestiones relacionadas