2008-10-14 716 views
12

Estoy construyendo una aplicación C#/ASP.NET con un back-end SQL. Estoy en la fecha límite y estoy terminando mis páginas, fuera del campo izquierdo uno de mis diseñadores incorporó una búsqueda de texto completo en una de mis páginas. Mis "búsquedas" hasta este momento han sido filtros, pudiendo restringir un conjunto de resultados por ciertos factores y valores de columna.¿Qué tan difícil es incorporar la búsqueda de texto completo con SQL Server?

Siendo que estoy en la fecha límite (sabes 3 horas de sueño por noche, en el punto en que parezco algo que el gato comió y vomitó), esperaba que esta página fuera muy similar a otras y Estoy tratando de decidir si apestar o no. Nunca antes había hecho una búsqueda de texto completo en una página ... ¿es una montaña para escalar o hay una solución simple?

gracias.

Respuesta

27

En primer lugar, es necesario habilitado búsqueda de texto completo de indexación en los servidores de producción, por lo que si no está dentro del alcance, no querrá ir con esto.

Sin embargo, si eso ya está listo, la búsqueda de texto completo es relativamente simple.

T-SQL tiene 4 predicados utilizados para la búsqueda de texto completo:

  • FREETEXT
  • FREETEXTTABLE
  • CONTIENE
  • CONTAINSTABLE

FREETEXT es el más simple, y se puede hacer como este:

SELECT UserName 
FROM Tbl_Users 
WHERE FREETEXT (UserName, 'bob') 

Results: 

JimBob 
Little Bobby Tables 

FREETEXTTABLE funciona igual que FreeTEXT, excepto que devuelve los resultados como una tabla.

El poder real de búsqueda de texto completo de T-SQL proviene de la CONTIENE (y CONTAINSTABLE) predicado ... Éste es enorme, así que voy a pegar su uso en:

CONTAINS 
    ({ column | * } , '<contains_search_condition>' 
    ) 

<contains_search_condition> ::= 
     { <simple_term> 
     | <prefix_term> 
     | <generation_term> 
     | <proximity_term> 
     | <weighted_term> 
     } 
     | { (<contains_search_condition>) 
     { AND | AND NOT | OR } <contains_search_condition> [ ...n ] 
     } 

<simple_term> ::= 
    word | " phrase " 

< prefix term > ::= 
    { "word * " | "phrase * " } 

<generation_term> ::= 
    FORMSOF (INFLECTIONAL , <simple_term> [ ,...n ]) 

<proximity_term> ::= 
    { <simple_term> | <prefix_term> } 
    { { NEAR | ~ } { <simple_term> | <prefix_term> } } [ ...n ] 

<weighted_term> ::= 
    ISABOUT 
     ({ { 
       <simple_term> 
       | <prefix_term> 
       | <generation_term> 
       | <proximity_term> 
       } 
      [ WEIGHT (weight_value) ] 
      } [ ,...n ] 
     ) 

Estos medios se puede escribir consultas tales como:

SELECT UserName 
FROM Tbl_Users 
WHERE CONTAINS(UserName, '"little*" NEAR tables') 

Results: 

Little Bobby Tables 

Buena suerte :)

+4

Lo he votado y lo he excluido como la respuesta no solo porque es una respuesta excelente y detallada, sino también por la referencia xkcd. GANAR. –

0

"¿Qué tan difícil es?" Es una pregunta difícil de responder. Por ejemplo, alguien que ya lo ha hecho 10 veces probablemente piense que es muy fácil. Todo lo que puedo decir es que es probable que lo encuentre mucho más fácil si utiliza algo como NLucene en lugar de hacer el suyo.

2

La búsqueda de texto completo en SQL Server es realmente fácil, un poco de configuración y un ligero ajuste en la consulta y ¡ya está listo para empezar! Lo he hecho para los clientes en menos de 20 minutos antes, estar familiarizado con el proceso

Aquí está el 2008 MSDN article, enlaces están con las versiones 2005 a partir de ahí

2

he utilizado dtSearch antes de la adición de búsqueda de texto completo a los archivos y bases de datos, y su material es bastante barato y fácil t o uso.

En lugar de agregar todo eso y configurar SQL, este script buscará en todas las columnas de una base de datos y le dirá qué columnas contienen los valores que está buscando.Sé que no es la solución "adecuada", pero puede que le compre algo de tiempo.

/*This script will find any text value in the database*/ 
/*Output will be directed to the Messages window. Don't forget to look there!!!*/ 

SET NOCOUNT ON 
DECLARE @valuetosearchfor varchar(128), @objectOwner varchar(64) 
SET @valuetosearchfor = '%staff%' --should be formatted as a like search 
SET @objectOwner = 'dbo' 

DECLARE @potentialcolumns TABLE (id int IDENTITY, sql varchar(4000)) 

INSERT INTO @potentialcolumns (sql) 
SELECT 
    ('if exists (select 1 from [' + 
    [tabs].[table_schema] + '].[' + 
    [tabs].[table_name] + 
    '] (NOLOCK) where [' + 
    [cols].[column_name] + 
    '] like ''' + @valuetosearchfor + ''') print ''SELECT * FROM [' + 
    [tabs].[table_schema] + '].[' + 
    [tabs].[table_name] + 
    '] (NOLOCK) WHERE [' + 
    [cols].[column_name] + 
    '] LIKE ''''' + @valuetosearchfor + '''''' + 
    '''') as 'sql' 
FROM information_schema.columns cols 
    INNER JOIN information_schema.tables tabs 
     ON cols.TABLE_CATALOG = tabs.TABLE_CATALOG 
      AND cols.TABLE_SCHEMA = tabs.TABLE_SCHEMA 
      AND cols.TABLE_NAME = tabs.TABLE_NAME 
WHERE cols.data_type IN ('char', 'varchar', 'nvchar', 'nvarchar','text','ntext') 
    AND tabs.table_schema = @objectOwner 
    AND tabs.TABLE_TYPE = 'BASE TABLE' 
ORDER BY tabs.table_catalog, tabs.table_name, cols.ordinal_position 

DECLARE @count int 
SET @count = (SELECT MAX(id) FROM @potentialcolumns) 
PRINT 'Found ' + CAST(@count as varchar) + ' potential columns.' 
PRINT 'Beginning scan...' 
PRINT '' 
PRINT 'These columns contain the values being searched for...' 
PRINT '' 
DECLARE @iterator int, @sql varchar(4000) 
SET @iterator = 1 
WHILE @iterator <= (SELECT Max(id) FROM @potentialcolumns) 
BEGIN 
    SET @sql = (SELECT [sql] FROM @potentialcolumns where [id] = @iterator) 
    IF (@sql IS NOT NULL) and (RTRIM(LTRIM(@sql)) <> '') 
    BEGIN 
     --SELECT @sql --use when checking sql output 
     EXEC (@sql) 
    END 
    SET @iterator = @iterator + 1 
END 

PRINT '' 
PRINT 'Scan completed' 
1

He estado allí. Funciona como un amuleto hasta que comienza a considerar la escalabilidad y funcionalidades de búsqueda avanzada como la búsqueda en múltiples columnas, dando a cada uno diferentes valores de peso.

Por ejemplo, la única manera de buscar más de Título y Resumen columnas es tener una columna calculada con SearchColumn = CONCAT(Title, Summary) y el índice sobre SearchColumn. ¿Ponderación? SearchColumn = CONCAT(CONCAT(Title,Title), Summary) algo así. ;) Filtrado? Olvídalo.

Cuestiones relacionadas