2012-03-27 1 views
6

Ho fatto una domanda simile un paio di mesi fa. Situato qui: MySQL Query based on stringConfronto delle stringhe MySQL

Il problema che sto incontrando è che questo funziona in un solo ordine particolare e in alcuni casi funziona troppo bene.

Ecco un frammento dei dati che questa query sta filtrando fuori (duplicati sono destinati, i dati reali):

- 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 

Quello query che ha aiutato nella mia ultima domanda (MySQL Query based on string) ha lavorato bene per un caso e non è riuscito per l'altra istanza. Ecco la domanda:

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

che restituisce:

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

Il problema di questo è che, come si può vedere rimosso una città che non dovrebbe avere perché è stato solo usando la prima parola per raggrupparlo. Quello rimosso era:

- EAST ROCKAWAY 

E 'stato GROUP'ed DA EST.

Mentre continuo a scrivere questo, ritengo che sia quasi impossibile perché la posizione del nome della città statico rispetto alle parti variabili cambia continuamente. A meno che non sia possibile confrontare una certa quantità di caratteri. Che non è di gran lunga impeccabile. Se qualcuno pensa che possano avere qualche intuizione o abbia lavorato e realizzato una cosa del genere apprezzerò il feedback e la guida. Il risultato finale sarebbe:

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

Puoi generare manualmente un elenco di sinonimi comuni, come 'N' =' Nord', 'Hghts' =' Heights', ecc. – mellamokb

risposta

2

mio suggerimento sarà una query costoso, ma si spera che si potrebbe usare questo tipo di operazione per eseguire una "pulizia" occasionale dei dati in modo che non sarà richiesto ogni volta tu interroghi questi dati.

Si potrebbe considerare la ricerca in una formula Levenshtein distance ... che è una metrica di stringa per misurare la quantità di differenza tra due sequenze.

Per evitare di dover calcolare la distanza per un prodotto cartesiano del proprio tavolo, è possibile prima restringere il set di città e indirizzi da confrontare con un controllo sanitario più rapido ... come se iniziassero con la stessa lettera e hanno una lunghezza simile.

Inizialmente, Si potrebbe quindi iniziare da solo tornando record con una distanza molto piccola Levenshtein ... È quindi possibile selezionare una variazione delle partite è tornato da applicare agli altri record al fine di normalizzare i dati .

È quindi possibile aumentare gradualmente la distanza finché non si iniziano a ottenere troppi falsi positivi.

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

Non vedo come funzionerebbe. Non conta la distanza di 'N HIGHLAND HGTS' e' NORD HIGHLAND HEIGHTS' come 7, mentre la distanza di 'EAST ROCKAWAY' e' FAR ROCKAWAY' sarebbe solo 4. Sembra che ci sarebbe un grande numero di falsi positivi/falsi negativi per qualsiasi distanza selezionata. – mellamokb

+0

@mellamokb Hai assolutamente ragione ... guardando i suoi dati di esempio più da vicino, sarebbe fattibile solo se usato insieme a una serie di sinonimi comuni come suggerivi. –

1

Toughie ...

Mi piacerebbe sicuramente fare uso di suggerimento di Michael e buttare in che la possibilità di mantenere i nomi di luogo unico nel database.

Ciò consentirebbe di utilizzare solo il calcolo della distanza stringa per aggiungere nuovi posti. È quindi possibile gestire i posti assegnando un id_gruppo a quelle posizioni identificate da levenshtein.

Forse potresti utilizzare altri dati (come la geolocalizzazione) per ottimizzare ulteriormente l'associazione dei luoghi. L'up shot forse sta solo usando il nome del luogo potrebbe non essere la soluzione migliore al tuo problema ...