2010-06-26 7 views
10

Quiero comparar las palabras individuales de la entrada del usuario con palabras individuales de una columna en mi tabla.SQL: división de una columna en varias palabras para buscar la entrada del usuario

Por ejemplo, considere estas filas en mi mesa:

ID Name 
1 Jack Nicholson 
2 Henry Jack Blueberry 
3 Pontiac Riddleson Jack 

Considere que la entrada del usuario es 'Pontiac Jack'. Quiero asignar pesos/rangos para cada coincidencia, por lo que no puedo usar una manta LIKE (WHERE Name LIKE @SearchString).

Si Pontiac está presente en cualquier fila, quiero otorgarle 10 puntos. Cada partida de Jack obtiene otros 10 puntos, etc. Así que la fila 3 obtendría 20 puntos, y las filas 1 y 2 obtendrían 10.

He dividido la entrada del usuario en palabras individuales y las he almacenado en una tabla temporal @SearchWords (Palabra).

Pero no puedo encontrar una manera de tener una declaración SELECT que me permita combinar esto. Tal vez estoy haciendo esto de la manera incorrecta?

Saludos, WT

+1

¿Considera utilizar SQL SErver Búsqueda de texto completo? –

+0

Sí, lo hice, no funcionó bien para nosotros, y fue muy difícil personalizarlo según nuestros requisitos. –

+1

+1 para búsqueda de texto completo, no necesariamente SQL Server, pero por ejemplo lucene.net. –

Respuesta

1

Para SQL Server, intente esto:

SELECT Word, COUNT(Word) * 10 AS WordCount 
FROM SourceTable 
INNER JOIN SearchWords ON CHARINDEX(SearchWords.Word, SourceTable.Name) > 0 
GROUP BY Word 
+0

Solución agradable y elegante. Imagino que la tabla del OP debería tener algo que vincule las palabras individuales con la frase de búsqueda original, por lo que obtener el puntaje de la frase completa sería tan simple como unir la frase a esto y sumar el conteo de palabras agrupadas por la frase completa. Niza nombre de usuario por cierto ...uno de mis xkcds favoritos :) –

0

¿Y esto? (Esta es la sintaxis de MySQL, creo que es suficiente con sustituir el CONCAT y hacerlo con +)

SELECT names.id, count(searchwords.word) FROM names, searchwords WHERE names.name LIKE CONCAT('%', searchwords.word, '%') GROUP BY names.id 

allí tendría que tener un resultado de SQL con el ID del nombre de la tabla y el recuento de las palabras que coinciden a esa identificación.

0

que podría hacerlo a través de una expresión de tabla común que funciona a cabo la ponderación. Por ejemplo:

--** Set up the example tables and data 
DECLARE @Name TABLE (id INT IDENTITY, name VARCHAR(50)); 
DECLARE @SearchWords TABLE (word VARCHAR(50)); 

INSERT INTO @Name 
     (name) 
VALUES ('Jack Nicholson') 
     ,('Henry Jack Blueberry') 
     ,('Pontiac Riddleson Jack') 
     ,('Fred Bloggs'); 

INSERT INTO @SearchWords 
     (word) 
VALUES ('Jack') 
     ,('Pontiac'); 

--** Example SELECT with @Name selected and ordered by words in @SearchWords 
WITH Order_CTE (weighting, id) 
AS (
    SELECT COUNT(*) AS weighting 
     , id 
     FROM @Name AS n 
     JOIN @SearchWords AS sw 
     ON n.name LIKE '%' + sw.word + '%' 
    GROUP BY id 
) 
SELECT n.name 
    , cte.weighting 
    FROM @Name AS n 
    JOIN Order_CTE AS cte 
    ON n.id = cte.id 
ORDER BY cte.weighting DESC; 

Usando esta técnica, también se puede aplicar un valor a cada palabra de búsqueda si quería. Entonces podrías hacer que Jack sea más valioso que Pontiac. Esto sería algo como esto:

--** Set up the example tables and data 
DECLARE @Name TABLE (id INT IDENTITY, name VARCHAR(50)); 
DECLARE @SearchWords TABLE (word VARCHAR(50), value INT); 

INSERT INTO @Name 
     (name) 
VALUES ('Jack Nicholson') 
     ,('Henry Jack Blueberry') 
     ,('Pontiac Riddleson Jack') 
     ,('Fred Bloggs'); 

--** Set up search words with associated value 
INSERT INTO @SearchWords 
     (word, value) 
VALUES ('Jack',10) 
     ,('Pontiac',20) 
     ,('Bloggs',40); 


--** Example SELECT with @Name selected and ordered by words and values in @SearchWords 
WITH Order_CTE (weighting, id) 
AS (
    SELECT SUM(sw.value) AS weighting 
     , id 
     FROM @Name AS n 
     JOIN @SearchWords AS sw 
     ON n.name LIKE '%' + sw.word + '%' 
    GROUP BY id 
) 
SELECT n.name 
    , cte.weighting 
    FROM @Name AS n 
    JOIN Order_CTE AS cte 
    ON n.id = cte.id 
ORDER BY cte.weighting DESC;  
0

Me parece que lo mejor que puede hacer sería mantener una tabla separada con todas las palabras individuales. Por ejemplo:

ID  Word  FK_ID 
1  Jack  1 
2  Nicholson 1 
3  Henry  2 
(etc) 

Esta tabla se mantiene al día con los desencadenantes, y que tendría un índice no agrupado en 'Palabra', 'FK_ID'. Entonces, el SQL para producir sus ponderaciones sería simple y eficiente.

0

¿Qué tal algo como esto ....

Select id, MAX(names.name), count(id)*10 from names 
inner join @SearchWords as sw on 
    names.name like '%'+sw.word+'%' 
group by id 

el supuesto de que la tabla con los nombres de los llamados "nombres".

Cuestiones relacionadas