2011-09-01 3 views
1

J'ai une table avec une colonne comme nvarchar(max) avec du texte extrait de documents Word. Comment puis-je créer une requête de sélection que je vais passer une autre liste de mots-clés en tant que paramètre et retourner les lignes triées par le nombre de correspondances?Requête SQL pour faire correspondre les mots-clés?

Peut-être que c'est possible avec une recherche plein texte?

+2

Avez-vous déjà essayé quelque chose? – msarchet

+0

oui ... Je sélectionne toutes les lignes et je le fais manuellement en C# ... très lent pour un système de production – Gustav

+0

@Gustav Pouvez-vous montrer ce que vous avez essayé en C#? – reggie

Répondre

1

Oui, possible avec la recherche plein texte, et probablement la meilleure réponse. Pour une solution T-SQL directe, vous pouvez utiliser une fonction de division et joindre, par ex. en supposant une table de numéros appelés dbo.Numbers (vous devrez peut-être décider d'une autre limite supérieure):

SET NOCOUNT ON; 
DECLARE @UpperLimit INT; 
SET @UpperLimit = 200000; 

WITH n AS 
(
    SELECT 
     rn = ROW_NUMBER() OVER 
     (ORDER BY s1.[object_id]) 
    FROM sys.objects AS s1 
    CROSS JOIN sys.objects AS s2 
    CROSS JOIN sys.objects AS s3 
) 
SELECT [Number] = rn - 1 
INTO dbo.Numbers 
FROM n 
WHERE rn <= @UpperLimit + 1; 

CREATE UNIQUE CLUSTERED INDEX n ON dbo.Numbers([Number]); 

Et une fonction de séparation qui utilise cette table des nombres:

CREATE FUNCTION dbo.SplitStrings 
(
    @List NVARCHAR(MAX) 
) 
RETURNS TABLE 
AS 
    RETURN 
    (
     SELECT DISTINCT 
      [Value] = LTRIM(RTRIM(
       SUBSTRING(@List, [Number], 
       CHARINDEX(N',', @List + N',', [Number]) - [Number]))) 
     FROM 
      dbo.Numbers 
     WHERE 
      Number <= LEN(@List) 
      AND SUBSTRING(N',' + @List, [Number], 1) = N',' 
    ); 
GO 

Ensuite, vous pouvez dire simplement:

SELECT key, NvarcharColumn /*, other cols */ 
FROM dbo.table AS outerT 
WHERE EXISTS 
(
    SELECT 1 
    FROM dbo.table AS t 
    INNER JOIN dbo.SplitStrings(N'list,of,words') AS s 
    ON t.NvarcharColumn LIKE '%' + s.Item + '%' 
    WHERE t.key = outerT.key 
); 

en tant que procédure:

CREATE PROCEDURE dbo.Search 
    @List NVARCHAR(MAX) 
AS 
BEGIN 
    SET NOCOUNT ON; 

    SELECT key, NvarcharColumn /*, other cols */ 
    FROM dbo.table AS outerT 
    WHERE EXISTS 
    (
     SELECT 1 
     FROM dbo.table AS t 
     INNER JOIN dbo.SplitStrings(@List) AS s 
     ON t.NvarcharColumn LIKE '%' + s.Item + '%' 
     WHERE t.key = outerT.key 
    ); 
END 
GO 

Ensuite, vous pouvez simplement passer en @List (par ex. EXEC dbo.Search @List = N'foo,bar,splunge') à partir de C#.

Ce ne sera pas super rapide, mais je suis sûr que ce sera plus rapide que d'extraire toutes les données en C# et de les boucler manuellement.

+0

merci! Mais qu'est-ce que c'est UpperLimit ??? – Gustav

+0

La longueur de la chaîne la plus longue que vous prévoyez de transmettre à @List. Ou le plus grand nombre que vous prévoyez d'utiliser à d'autres fins (les tables Numbers sont très pratiques pour de nombreuses fonctions - voir http://sqlserver2000.databases.aspfaq.com/why-should-i-consider-using-an-auxiliary-numbers- table.html).En cas de doute, ne créez pas la table Numbers à chaque fois que vous exécutez la requête. La table et la fonction Numbers ne doivent être créées qu'une seule fois. –

0

how to ... return the rows ordered by the number of [full-text] matches

Je ne l'ai pas utilisé moi-même, mais croient SQL Server 2008 prend en charge pondération l'CONTAINSTABLE correspond à ce qui pourrait être utile pour vous:

  http://msdn.microsoft.com/en-us/library/ms189760.aspx 

Si vous ne disposez pas d'un moteur qui renvoie des résultats pondérés par le nombre de résultats ...

Vous pourriez écrire un UDF qui prend deux entrées et retourne un entier: la grande valeur de texte est la première entrée et les mots y Vous cherchez une chaîne délimitée par des virgules, c'est la seconde. La fonction renvoie un nombre entier représentant le nombre de mots recherchés trouvés au moins une fois dans le texte, ou le nombre total de fois que les mots recherchés ont été trouvés. La mise en œuvre - comment peser - dépend de vous. Peut-être, par exemple, vous voudriez organiser les mots recherchés dans l'ordre le plus important ou le moins important, et donner à un mot important plus de poids qu'un mot moins important.

Vous pouvez ensuite utiliser votre moteur de recherche en texte intégral pour trouver tous les documents qui contiennent au moins un des mots (vous devriez OU eux), et que vous exécutez ce jeu de résultats par votre fonction scalaire UDF:

   pseudo code 

      select title, weightfunction(summary, 'word1,word2,word3....wordN') 
      from docs 
      where summary contains (word1 or word2 or word3 ... or wordN) 
      order by weightfunction(summary, 'word1,word2,word3....wordN') desc 
Questions connexes