2012-03-27 5 views
6

J'ai posé une question similaire il y a quelques mois. Situé ici: MySQL Query based on stringComparaison de chaînes MySQL

Le problème que je rencontre est que cela ne fonctionne que dans un ordre particulier et, dans certains cas, cela fonctionne trop bien.

Voici un extrait des données que cette requête est le filtrage (les doublons sont destinés, les données réelles):

- BELLMORE 
- ATLANTIC BCH 
- ATLANTIC BEACH 
- E HILLS 
- EAST HILLS 
- EAST ROCKAWAY 
- FAR ROCKAWAY 
- FLORAL PARK 
- FLORAL PARK 
- HIGHLAND HEIGHTS 
- N HIGHLAND HGTS 
- NORTH HIGHLAND HEIGHTS 

L'une requête qui a contribué à ma dernière question (MySQL Query based on string) a bien fonctionné pour une instance et échoué pour l'autre instance. Voici la requête:

select names from tablename group by substring_index(names," ",1) 

qui retourne:

- BELLMORE 
- ATLANTIC BEACH 
- EAST HILLS 
- FAR ROCKAWAY 
- FLORAL PARK 
- HIGHLAND HEIGHTS 
- N HIGHLAND HGTS 
- NORTH HIGHLAND HEIGHTS 

Le problème que l'on est que vous pouvez le voir enlever une ville qu'il ne devrait pas avoir parce qu'il utilisait seulement le premier mot pour le grouper par. Celui qu'il a retiré était:

- EAST ROCKAWAY 

Il a été GROUPÉ PAR L'EST. Comme je continue à écrire ceci, je pense que c'est presque impossible parce que la position du nom de ville statique par rapport aux parties variables est toujours changeante. Sauf si vous pouvez comparer un certain nombre de caractères. Ce qui n'est pas sans faille de loin. Si quelqu'un pense avoir un aperçu ou avoir travaillé et accompli une telle chose, j'apprécierai les commentaires et les conseils. Le résultat de fin serait:

- BELLMORE 
- ATLANTIC BEACH 
- EAST HILLS 
- EAST ROCKAWAY 
- FAR ROCKAWAY 
- FLORAL PARK 
- HIGHLAND HEIGHTS 
+1

Pouvez-vous générer manuellement une liste de synonymes communs, tels que 'N' =' North', 'Hghts' =' Heights', etc. – mellamokb

Répondre

2

Ma suggestion sera une requête coûteuse, mais nous espérons que vous pouvez utiliser ce type d'opération pour effectuer une occasionnelle « nettoyage » de vos données afin qu'il ne sera pas nécessaire chaque fois vous interrogez ces données.

Vous pouvez envisager de rechercher dans une formule Levenshtein distance ... qui est une mesure de chaîne pour mesurer la quantité de différence entre deux séquences. Afin d'éviter de devoir calculer la distance pour un produit cartésien de votre table, vous pouvez d'abord restreindre l'ensemble des villes et des adresses à comparer avec un contrôle de cohérence plus rapide ... telles qu'elles commencent par la même lettre , et ont une longueur similaire.

Dans un premier temps, vous pouvez alors commencer Vous pourriez alors que de retour des dossiers avec une très petite Levenshtein ... sélectionner une variation des résultats est retourné à appliquer aux autres dossiers afin de normaliser vos données .

Vous pouvez ensuite augmenter progressivement la distance jusqu'à ce que vous commenciez à avoir trop de faux positifs.

Here's an implementation directly in MySql:

CREATE FUNCTION levenshtein(s1 VARCHAR(255), s2 VARCHAR(255)) 
    RETURNS INT 
    DETERMINISTIC 
    BEGIN 
    DECLARE s1_len, s2_len, i, j, c, c_temp, cost INT; 
    DECLARE s1_char CHAR; 
    -- max strlen=255 
    DECLARE cv0, cv1 VARBINARY(256); 
    SET s1_len = CHAR_LENGTH(s1), s2_len = CHAR_LENGTH(s2), cv1 = 0x00, j = 1, i = 1, c = 0; 
    IF s1 = s2 THEN 
     RETURN 0; 
    ELSEIF s1_len = 0 THEN 
     RETURN s2_len; 
    ELSEIF s2_len = 0 THEN 
     RETURN s1_len; 
    ELSE 
     WHILE j <= s2_len DO 
     SET cv1 = CONCAT(cv1, UNHEX(HEX(j))), j = j + 1; 
     END WHILE; 
     WHILE i <= s1_len DO 
     SET s1_char = SUBSTRING(s1, i, 1), c = i, cv0 = UNHEX(HEX(i)), j = 1; 
     WHILE j <= s2_len DO 
      SET c = c + 1; 
      IF s1_char = SUBSTRING(s2, j, 1) THEN 
      SET cost = 0; ELSE SET cost = 1; 
      END IF; 
      SET c_temp = CONV(HEX(SUBSTRING(cv1, j, 1)), 16, 10) + cost; 
      IF c > c_temp THEN SET c = c_temp; END IF; 
      SET c_temp = CONV(HEX(SUBSTRING(cv1, j+1, 1)), 16, 10) + 1; 
      IF c > c_temp THEN 
       SET c = c_temp; 
      END IF; 
      SET cv0 = CONCAT(cv0, UNHEX(HEX(c))), j = j + 1; 
     END WHILE; 
     SET cv1 = cv0, i = i + 1; 
     END WHILE; 
    END IF; 
    RETURN c; 
    END; 
+0

Je ne vois pas comment cela pourrait fonctionner. Ne compte-t-il pas la distance de «N HIGHLAND HGTS» et «NORTH HIGHLAND HEIGHTS» comme 7, alors que la distance de «EAST ROCKAWAY» et «FAR ROCKAWAY» ne serait que de 4. On dirait qu'il y aurait une très grande nombre de faux positifs/faux négatifs pour toute distance que vous sélectionnez. – mellamokb

+0

@mellamokb Vous avez tout à fait raison ... en regardant de plus près les données de son échantillon, ce ne serait faisable que s'il était utilisé avec un ensemble de synonymes communs comme vous l'avez suggéré. –

1

toughie ...

Je serais certainement faire usage de suggestion de Michael et jette en ce que la possibilité de maintenir des noms de lieux uniques dans la base de données.Cela vous permet d'utiliser uniquement le calcul de la distance de chaîne pour l'ajout de nouveaux emplacements. Vous pouvez ensuite gérer les lieux en affectant un ID_associé aux endroits identifiés par le levenshtein. Peut-être pourriez-vous utiliser d'autres données (comme la géolocalisation) pour affiner la façon dont vous associez des lieux. Le coup vers le haut est peut-être simplement en utilisant le nom de lieu ne peut pas être la meilleure solution à votre problème ...