2008-11-21 13 views
6

Actualmente tengo una tabla que busco en 4 campos, FirstName, LastName, MiddleName, y AKA. Actualmente tengo una búsqueda CONTAINSTABLE para las filas y funciona. No está bien, pero funciona. Ahora Quiero hacer que los nombres ponderados más arriba y segundo nombre más bajo.SQL Server Weighted Full Text Search

Encontré el comando ISABOUT pero parece bastante inútil si tengo que hacerlo por palabra, no por columna (con suerte lo entendí mal). Esta no es una opción si es de palabra porque no sé cuántas palabras ingresará el usuario.

Encontré el hilo here que habla de esta misma solución, sin embargo, no pude lograr que la solución aceptada funcione. Tal vez he hecho algo mal, pero independientemente de que no pueda hacer que funcione, y su lógica parece realmente ... rara. Tiene que haber una manera más fácil.

Respuesta

6

La clave para manipular los rankings es utilizar una unión. Para cada columna, usa una instrucción select separada. En esa declaración, agregue un identificador que muestre desde qué columna se extrajo cada fila. Inserte los resultados en una variable de tabla, luego puede manipular la clasificación clasificando el identificador o multiplicando el rango por algún valor basado en el identificador.

La clave es dar la apariencia de modificar la clasificación, no cambiar realmente la clasificación del servidor SQL.

Ejemplo utilizando una variable de tabla:

DECLARE @Results TABLE (PersonId Int, Rank Int, Source Int) 

para la tabla personas con columnas PersonId Int PK Identity, FirstName VarChar(100), MiddleName VarChar(100), LastName VarChar(100), AlsoKnown VarChar(100) con cada columna añadió a un catálogo de texto completo, se puede utilizar la consulta:

INSERT INTO @Results (PersonId, Rank, Source) 

SELECT PersonId, Rank, 1 
FROM ContainsTable(People, FirstName, @SearchValue) CT INNER JOIN People P ON CT.Key = P.PersonId 

UNION 
SELECT PersonId, Rank, 2 
FROM ContainsTable(People, MiddleName, @SearchValue) CT INNER JOIN People P ON CT.Key = P.PersonId 

UNION 
SELECT PersonId, Rank, 3 
FROM ContainsTable(People, LastName, @SearchValue) CT INNER JOIN People P ON CT.Key = P.PersonId 

UNION 
SELECT PersonId, Rank, 4 
FROM ContainsTable(People, AlsoKnown, @SearchValue) CT INNER JOIN People P ON CT.Key = P.PersonId 

/* 
Now that the results from above are in the @Results table, you can manipulate the 
rankings in one of several ways, the simplest is to pull the results ordered first by Source then by Rank. Of course you would probably join to the People table to pull the name fields. 
*/ 

SELECT PersonId 
FROM @Results 
ORDER BY Source, Rank DESC 

/* 
A more complex manipulation would use a statement to multiply the ranking by a value above 1 (to increase rank) or less than 1 (to lower rank), then return results based on the new rank. This provides more fine tuning, since I could make first name 10% higher and middle name 15% lower and leave last name and also known the original value. 
*/ 

SELECT PersonId, CASE Source WHEN 1 THEN Rank * 1.1 WHEN 2 THEN Rank * .9 ELSE Rank END AS NewRank FROM @Results 
ORDER BY NewRank DESC 

El único inconveniente es Notarás que no usé UNION ALL, por lo que si aparece una palabra en más de una columna, el rango no reflejará eso. Si eso es un problema, puede usar UNION ALL y luego eliminar identificaciones de personas duplicadas agregando todo o parte del rango del registro duplicado al rango de otro registro con la misma identificación de persona.

+0

¡Esto es extremadamente útil! Es una pena que esto no esté incorporado en SQL Server. –

+0

sin embargo, esto no resume la clasificación ... – yeeen

2

Los rangos son inútiles en los índices, no puede fusionarlos y espera que el resultado tenga algún significado. Los números de rango de cada índice son comparaciones de manzana/naranja/uva/sandía/par que no tienen relativo que significa contenido WRT de otros índices.

Claro que puedes tratar de vincular/ponderar/ordenar rangos entre índices para intentar obtener un resultado significativo, pero al final del día el resultado sigue siendo un galimatías pero posiblemente lo suficientemente bueno como para proporcionar una solución viable dependiendo de los detalles de tu situación

En mi opinión, la mejor solución es poner todos los datos que desea buscar en un solo índice/columna FTS y usar ese rango de columnas para pedir su resultado. Incluso si tiene que duplicar los contenidos del campo para lograr el resultado .

0

Supongo que los datos devueltos se unen a otras tablas dentro de su esquema? Desarrollaría su propio RANGO basado en columnas de datos asociados al índice de texto completo. Esto también proporciona un nivel garantizado de precisión en el valor de RANGO.

2

Hace solo unas semanas estaba resolviendo un problema muy similar y la solución es sorprendentemente fácil (aunque fea y consume mucho espacio). Cree otra columna que contenga valores combinados de FirstName + FirstName + LastName + MiddleName en este orden.La columna Duplicate FirstName es , no es un error tipográfico, es un truco para forzar FT a valores de peso de FirstName más altos durante la búsqueda.

0

¿Qué hay de esta manera:

SELECT p.* from Person p 
left join ContainsTable(Person, FirstName, @SearchValue) firstnamefilter on firstnamefiler.key = p.id 
left join ContainsTable(Person, MiddleName, @SearchValue) middlenamefilter on middlenamefilter.key = p.id 
where (firstnamefilter.rank is not null or middlenamefilter.rank is not null) 
order by firstnamefilter.rank desc, middlenamefilter.rank desc 

Esto producirá un registro para cada registro Person donde ya sea el primero o el segundo nombre (o ambos) partido en el término de búsqueda, y el fin de todos los partidos contra el primer nombre primero (en orden descendente), seguido de todos los partidos contra el segundo nombre (nuevamente en orden descendente)