2012-03-27 25 views
6

Hice una pregunta similar hace un par de meses. Situado aquí: MySQL Query based on stringComparación de cadenas MySQL

El problema que me encuentro es que esto funciona en un solo orden en particular y en algunos casos funciona demasiado bien.

Aquí hay un fragmento de los datos que esta consulta está filtrando (duplicados están destinados, los datos reales):

- 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 

La única consulta que ayudó en mi última pregunta (MySQL Query based on string) trabajó bien para una instancia y falló para la otra instancia. Ésta es la consulta:

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

que devuelve:

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

El problema con esto es que, como se puede ver que se elimine una ciudad que no debería tener, ya que sólo se utilizaba la primera palabra para agruparlo por. El que eliminó fue:

- EAST ROCKAWAY 

Fue GROUP'ed BY EAST.

Mientras continúo escribiendo esto, siento que es casi imposible porque la posición del nombre de la ciudad estática frente a las partes variables siempre está cambiando. A menos que puedas comparar una cierta cantidad de personajes. Lo cual no es perfecto por lejos. Si alguien piensa que puede tener alguna idea o ha trabajado y logrado tal cosa, agradeceré la retroalimentación y la orientación. El resultado final sería:

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

Puede generar manualmente una lista de sinónimos comunes, como 'N' =' North', 'Hghts' =' Heights', etc. – mellamokb

Respuesta

2

Mi sugerencia habrá una consulta caro, pero es de esperar que podría utilizar este tipo de operación a realizar una "limpieza" de vez en cuando de sus datos de modo que no se requiere cada vez usted consulta esta información

Puede considerar buscar en una fórmula Levenshtein distance ... que es una métrica de cadena para medir la cantidad de diferencia entre dos secuencias.

Para evitar la necesidad de calcular la distancia para un producto cartesiano de su mesa, primero puede reducir el conjunto de ciudades y direcciones para compararlas con un control de cordura más rápido ... como que comienzan con la misma letra y tienen una longitud similar.

Inicialmente, A continuación, podría empezar por devolver solamente los registros con una distancia muy pequeña Levenshtein ... A continuación, puede seleccionar una variación de los partidos volvió a aplicarse a los otros registros con el fin de normalizar sus datos .

Puede aumentar gradualmente la distancia hasta que empiece a recibir demasiados falsos positivos.

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

No veo cómo funcionaría esto. ¿No va a contar la distancia de 'N HIGHLAND HGTS' y' NORTH HIGHLAND HEIGHTS' como 7, mientras que la distancia de 'EAST ROCKAWAY' y 'FAR ROCKAWAY' solo sería 4. Parece que habría una gran número de falsos positivos/falsos negativos para cualquier distancia que seleccione. – mellamokb

+0

@mellamokb Tiene toda la razón ... al observar los datos de su muestra más de cerca, solo sería factible si se utilizara junto con un conjunto de sinónimos comunes como el que sugirió. –

1

Toughie ...

sin duda me hago uso de la sugerencia de Michael y meta en la que la posibilidad de mantener los nombres de lugares únicos en la base de datos.

Esto le permitiría usar solo el cálculo de distancia de cuerda al agregar lugares nuevos. A continuación, puede administrar los lugares asignando un associate_id a los lugares que identifique levenshtein.

Quizás podría utilizar algunos otros datos (como la geolocalización) para seguir afinando la forma de asociar lugares. La oportunidad es tal vez solo usar el nombre del lugar puede no ser la mejor solución para su problema ...

Cuestiones relacionadas