web-dev-qa-db-fra.com

Correspondance floue SQL

J'espère que je ne répète pas cette question. J'ai fait quelques recherches ici et google avant de poster ici.

J'utilise un eStore avec SQL Server 2008R2 avec le texte intégral activé.

Mes exigences,

  1. Il y a une table de produit, qui a le nom du produit, les codes OEM, le modèle dans lequel ce produit s'intègre. Tous sont en texte.
  2. J'ai créé une nouvelle colonne appelée TextSearch. Cela a des valeurs concaténées de nom de produit, de code OEM et de modèle dans lesquelles ce produit s'intègre. Ces valeurs sont séparées par des virgules.
  3. Lorsqu'un client saisit un mot clé, nous effectuons une recherche dans la colonne TextSearch pour trouver des produits. Voir la logique de correspondance ci-dessous.

J'utilise un Fulltext hybride et j'aime faire une recherche normale. Cela donne des résultats plus pertinents. Toutes les requêtes exécutées dans une table temporaire et distinctes ont été renvoyées.

Logique de correspondance,

  1. Exécutez le SQL suivant pour obtenir le produit pertinent en utilisant le texte intégral. Mais @Keywords sera prétraité. Dites "CLC 2200" sera changé en "CLC * ET 2200 *"

    SELECT Id FROM dbo.Product WHERE CONTAINS (TextSearch, @ Keywords)

  2. Une autre requête s'exécutera en utilisant normal like. Ainsi, "CLC 2200" sera prétraité en "TextSearch comme% clc% ET TextSearch comme% 2200%". C'est simplement parce que la recherche en texte intégral ne recherchera pas les modèles avant les mots clés. par exemple, il ne renverra pas 'pclc 2200'.

    SELECT ID FROM dbo.Product WHERE TextSearch like '% clc%' AND TextSearch like '% 2200%'

  3. Si les étapes 1 et 2 n'ont renvoyé aucun enregistrement, la recherche suivante sera exécutée. La valeur 135 a été affinée par moi pour renvoyer des enregistrements plus pertinents.

    SELECT p.id FROM dbo.Product AS p INNER JOIN FREETEXTTABLE (produit, TextSearch, @ Keywords) AS r ON p.Id = r. [KEY] WHERE r.RANK> 135

Tous les éléments combinés ci-dessus fonctionnent correctement à une vitesse raisonnable et renvoient des produits pertinents pour les mots clés.

Mais je cherche à m'améliorer encore quand aucun produit n'est trouvé.

Dites si le client recherche "CLC 2200npk" et que ce produit n'était pas là, je devais le montrer très près de "CLC 2200".

Jusqu'à présent, j'ai essayé d'utiliser la fonction Soundex () . Achetez la valeur soundex informatique pour chaque mot dans la colonne TextSearch et comparez avec la valeur soudex du mot clé. Mais cela renvoie beaucoup trop d'enregistrements et ralentit aussi.

exemple, 'CLC 2200npk' renverra des produits tels que 'CLC 1100' etc. Mais ce ne serait pas un bon résultat. Comme il n'est pas proche du CLC 2200npk

Il y en a un autre bon ici . mais cela utilise les fonctions CLR. Mais je ne peux pas installer les fonctions CLR sur le serveur.

Donc ma logique devrait être,

si 'CLC 2200npk' non trouvé, montrer à côté de 'CLC 2200' si 'CLC 2200' non trouvé, montrer à côté de 'CLC 1100'

Des questions

  1. Est-il possible de faire correspondre comme suggéré?
  2. Si je devais faire une correction orthographique et une recherche, quelle serait la bonne façon? Toute notre liste de produits est en anglais.
  3. Existe-t-il des UDF ou SP pour faire correspondre les textes comme mes suggestions?

Merci.

11
Jeyara

Une solution spécifique au domaine plutôt rapide peut être de calculer une similitude de chaîne à l'aide de SOUNDEX et une distance numérique entre 2 chaînes. Cela ne vous sera vraiment utile que si vous avez beaucoup de codes de produit.

En utilisant un UDF simple comme ci-dessous, vous pouvez extraire les caractères numériques d'une chaîne de sorte que vous puissiez ensuite obtenir 2200 de 'CLC 2200npk' et 1100 de 'CLC 1100' afin que vous puissiez maintenant déterminer la proximité en fonction de la sortie SOUNDEX de chaque entrée ainsi que la proximité de la composante numérique de chaque entrée.

CREATE Function [dbo].[ExtractNumeric](@input VARCHAR(1000))
RETURNS INT
AS
BEGIN
    WHILE PATINDEX('%[^0-9]%', @input) > 0
    BEGIN
        SET @input = STUFF(@input, PATINDEX('%[^0-9]%', @input), 1, '')
    END
    IF @input = '' OR @input IS NULL
        SET @input = '0'
    RETURN CAST(@input AS INT)
END
GO

En ce qui concerne les algorithmes à usage général, il en existe quelques-uns qui pourraient vous aider avec plus ou moins de succès en fonction de la taille de l'ensemble de données et des exigences de performances. (les deux liens ont des implémentations TSQL disponibles)

  • Double Metaphone - Cet algo vous donnera une meilleure correspondance que soundex au prix de la vitesse, il est vraiment bon pour la correction d'orthographe.
  • Levenshtein Distance - Cela calculera le nombre de touches qu'il faudrait pour transformer une chaîne en une autre, par exemple pour passer de 'CLC 2200npk' à 'CLC 2200' est 3, tandis que de 'CLC 2200npk' à ' CLC 1100 'est 5.

Ici est un article intéressant qui applique les deux algos ensemble, ce qui peut vous donner quelques idées.

Et bien, j'espère que cela aide un peu.

EDIT: ici est une implémentation partielle beaucoup plus rapide de la distance de Levenshtein (lire l'article qui ne renverra pas exactement les mêmes résultats que celui normal). Sur ma table de test de 125 000 lignes, il s'exécute en 6 secondes, contre 60 secondes pour la première à laquelle je suis lié.

18
David Ewen