2

J'ai une table de base de données Microsoft SQL Server avec environ 7 millions d'enregistrements de sources multiples, contenant principalement une valeur de nom de chaîne avec des détails connexes. Pour presque tous les enregistrements, il semble qu'il y ait une douzaine d'enregistrements de typo similaires et j'essaie de faire des correspondances floues pour identifier des groupes d'enregistrements tels que "Apple", "Aple", "Pommes", "Spple", etc. contenir plusieurs mots avec des espaces entre eux. J'ai proposé une solution d'utilisation d'une fonction scalaire edit-distance qui retourne le nombre de touches nécessaires pour la transformation de string1 en string2 et en utilisant cette fonction pour rejoindre la table à elle-même. Comme vous pouvez l'imaginer, cela ne fonctionne pas bien car il faut exécuter la fonction des millions de fois pour évaluer une jointure. Donc, je mets cela dans un curseur pour qu'au moins une seule chaîne1 soit évaluée à la fois, cela obtient au moins des résultats mais après l'avoir laissé pendant des semaines, elle n'a réussi qu'à évaluer 150 000 enregistrements. Avec 7 millions à évaluer, je ne pense pas avoir le temps que ma méthode va prendre. J'ai mis des index de texte intégral sur les noms de chaîne, mais je n'ai pas vraiment trouvé un moyen d'utiliser les prédicats de texte intégral quand je n'avais pas une valeur statique que je recherchais.Comment trouver des lignes dans un tableau avec des valeurs de chaîne similaires

Des idées comment je pourrais faire quelque chose comme le suivant d'une manière qui ne prendrait pas des mois à courir?

SELECT t1.name, t2.name 
FROM names AS t1 
INNER JOIN names AS t2 
     ON EditDistance(t1.name,t2.name) = 1 
     AND t1.id != t2.id 
+1

Cette question m'a rappelé la fonction [soundex] (https://technet.microsoft.com/en-us/library/ms189282 (v = sql.105) .aspx). – LukStorms

+0

Eh bien, le problème avec votre requête est que c'est presque comme une jointure cartésienne. Un 7 millions de croix se rejoignent sur 7 millions. Et sans possibilité d'utiliser un index. Aie. Peut-être que vous devriez créer une table avec tous les mots du dictionnaire connus. Et utilisez ce dictionnaire pour sélectionner uniquement les mots inconnus de votre table. – LukStorms

+0

Oui, la jointure cartésienne est définitivement le problème.L'approche du dictionnaire semble intéressante, mais une autre difficulté est que les noms sont en plusieurs langues et peuvent littéralement contenir n'importe quel mot ou combinaison de mots dans ces langues. Je ne suis pas sûr de pouvoir obtenir une source définitive à utiliser comme dictionnaire. – kscott

Répondre

0

Vous pouvez utiliser la fonction DIFFERENCE (character_expression , character_expression) pour évaluer la différence dans le code SOUNDEX pour chaque expression de caractères. Le code SOUNDEX est utilisé pour évaluer la différence entre les chaînes.

DIFFERENCE renverra un entier de 0 (la plus grande différence possible) et 4 (la différence la plus faible possible). Vous pouvez utiliser cette valeur pour déterminer à quel point les chaînes sont proches (par exemple, une condition similaire à DIFFERENCE(column1, column2) > 3 correspond aux enregistrements où les valeurs SOUNDEX de column1 et column2 sont désactivées de 1).

Voici un lien vers la documentation de la fonction DIFFERENCE: https://technet.microsoft.com/en-us/library/ms188753(v=sql.105).aspx

+0

J'ai essayé plusieurs fois d'utiliser SOUNDEX et je n'obtiens pas de résultats fiables. Je devrais être très confiant que les deux chaînes sont censées être identiques, mais j'ai vu plusieurs correspondances SOUNDEX que je ne peux pas expliquer – kscott

0

Vous devez trouver un moyen d'éviter de comparer chaque enregistrement à tout autre dossier. Si vous utilisez juste un seul champ, vous pouvez utiliser une structure de données spéciale comme une structure arborescente, par exemple https://github.com/mattandahalfew/Levenshtein_search

-1

SELECT t1.name des noms T1 où EditDistance (t1.name) = 1 SAUF

SELECT t2.name FROM noms t2 où EditDistance (t2.name) = 1

ou d'utiliser non dans SELECT t1.name FROM noms AS t1 où EditDistance (t1.name) = 1 et t1.ID pas dans (SELECT t2. ID FROM noms t2 où EditDistance (t2.name) = 1)

Puisque t1.id! = T2.id n'est pas une bonne pratique.

+0

Cela n'a peut-être pas été clair, mais EditDistance compare deux chaînes, vous ne pouvez pas l'utiliser avec une chaîne Je ne pense pas que le fait que ce ne soit pas égal dans la jointure soit n'importe où proche du plus gros problème de performance ici. – kscott