2011-06-11 12 views
6

Tengo una base de datos en la que estoy almacenando más de 1000000 nombres en mysql. Ahora la tarea de mi aplicación es un poco típica. No solo busco nombres en la base de datos, sino que también encuentro nombres similares. Supongamos que el nombre se ingresa como christian, luego la aplicación mostrará nombres sugeridos como christine, chris, etc. ¿Cuál es la forma óptima de hacerlo, sin utilizar la cláusula like? Las sugerencias serán solo sobre los cambios en la última parte del nombre.Forma óptima de encontrar un valor similar en una tabla grande

+0

¿Por qué no quieres usar la cláusula 'like'? – Geoffroy

+0

Considera cambiar a Postgres. Permite hacer esto usando [diccionarios de búsqueda de texto] (http://www.postgresql.org/docs/9.0/static/textsearch-dictionaries.html) –

+0

¿Puede agregar un nuevo campo? de ser así, verifique mi comentario adicional en mi respuesta. –

Respuesta

5

Si quieres también nombres similares (por sonido) algo así como SOUNDEX() podría ayudar: http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_soundex

De lo contrario … LIKE 'chri%' parece para mí no es una mala idea?

Si realmente quiere solo los primeros caracteres sin LIKE puede usar SUBSTRING().

+0

Desearía poder votar esta vez dos veces. Por supuesto, si usa SUBSTRING() para comparar solo los primeros caracteres, LIKE xyz% parece hacer lo mismo. Pero SOUNDEX() ... es una gran sugerencia y me recuerda a los módulos Lingua :: EN :: SimilarNames, Text :: Soundex y Lingua :: EN :: NameLookup CPAN para Perl (lo que no ayudaría porque requiere que el conjunto de datos sea importado primero). – DavidO

+1

usando SUBSTRING() requerirá un escaneo completo de la tabla. LIKE será más rápido en este caso. SOUNDEX() es una buena sugerencia, pero debe almacenarse como un campo indexado por separado para que la búsqueda sea rápida. –

0

Podría usar una expersión regular, creo. No estoy de acuerdo, pero hay una función llamada REGEXP que puedes poner en una cláusula WHERE. Mire here

+0

'REGEXP' es útil para consultas más complejas, pero será mucho más lento que' LIKE'. – glortho

+0

¡Me imaginé que (nunca lo usé) era solo para proponer algo diferente de "ME GUSTA"! –

1

Like es generalmente una buena solución, pero otra forma de mejorar el rendimiento para esto podría ser crear un índice de columna parcial y luego enviar consultas en la misma longitud que su prefijo. Consulte el MySQL documentation con respecto a col_name(length).

0

Puede usar SOUNDS LIKE, creo que debería ser bastante rápido también.

http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#operator_sounds-like

+1

kalyoncu, esto probablemente hará un buen trabajo, pero requerirá una exploración de tabla completa como SOUNDEX(). –

+0

Si puede crear un campo adicional, puede evitar eso. Con cada inserción inserta Soundex en ese campo y en el tiempo de búsqueda será bastante rápido. También puedes construir un índice en ese campo. Hmm, creo que esta es una mejor respuesta que la anterior. –

+0

También puede convertir una cadena de soundex en un número y está en formato C#### si no recuerdo mal. Donde C es entre 1-26, entonces un número de 6 dígitos como máximo. –

0

utilizando como en el lado izquierdo se fija no requerirá un recorrido de tabla. Supongo que esta es la razón por la que no desea utilizar LIKE: SELECT * FROM table WHERE name LIKE CONCAT(?, "%") es rápido y no requerirá un escaneo de tabla para encontrar filas. El CONCAT le permite usar consultas preparadas con% de sintaxis.

También podría hacer algo como:

SELECT * from table WHERE name < 'christian' LIMIT 20

y

SELECT * FROM table WHERE name > 'christian' LIMIT 20

encontrar vecinos en la lista ordenada.

2

Puede usar la función metaphone() de php para generar el código de metafonía para cada nombre y almacenarlos junto con los nombres.

<?php 
print "chris" . "\t" . metaphone("chris") . "\n"; 
print "christian" . "\t" . metaphone("christian") . "\n"; 
print "christine" . "\t" . metaphone("christine") . "\n"; 

# prints: 
# chris  XRS 
# christine XRSTN 
# christian XRSXN 

continuación, puede utilizar un algoritmo de distancia levenshtein (ya sea en php [http://php.net/manual/en/function.levenshtein.php] o MySQL [http://www.artfulsoftware.com /infotree/queries.php#552]) para calcular la distancia entre los metacódigos. En mi prueba a continuación, una distancia de 2 o menos parecía indicar el nivel de similitud que está buscando.

<?php 
$names = array(
     array('mike',metaphone('mike')), 
     array('chris',metaphone('chris')), 
     array('chrstian',metaphone('christian')), 
     array('christine',metaphone('christine')), 
     array('michelle',metaphone('chris')), 
     array('mick',metaphone('mick')), 
     array('john',metaphone('john')), 
     array('joseph',metaphone('joseph')) 
); 

foreach ($names as $name) { 
     _compare($name); 
} 

function _compare($n) { 
     global $names; 
     $name = $n[0]; 
     $meta = $n[1]; 

     foreach ($names as $cname) { 
       printf("The distance between $name and {$cname[0]} is %d\n",       
        levenshtein($meta, $cname[1])); 
     } 
} 
Cuestiones relacionadas