2009-12-21 18 views
5

Tengo una tabla que contiene 3 millones de registros de personas en la que quiero realizar una coincidencia difusa usando q-grams (en el apellido, por ejemplo). He creado una tabla de 2 gramos que enlaza a esto, pero el rendimiento de la búsqueda no es excelente en este volumen de datos (alrededor de 5 minutos).q-gram optimizaciones de coincidencia aproximadas

básicamente Tengo dos preguntas: (1) ¿Puede sugerir formas de mejorar el rendimiento para evitar un recorrido de tabla (es decir, tener que contar comunes q-gramos entre la cadena de búsqueda y 3 millones de apellidos) (2) Con q-grams, si A es similar a B y C es similar a B, ¿implica que C es similar a A?

Saludos cordiales

Peter

Respuesta

6

He estado buscando en la cadena difusa juego últimamente, por lo que incluso con el riesgo de responder a una pregunta abandonada, aquí va. Espero que encuentres esto útil.

Supongo que solo está interesado en las cadenas cuya distancia de edición es menor que un valor determinado. Y su Q-gramos (o n-gramos) se ven así

2-grams for "foobar": {"fo","oo","ob","ba","ar"} 
  1. Usted podría utilizar posicionales q-gramas:

    "foobar": {("fo",1),("oo",2),("ob",3),("ba",4),("ar",5)} 
    

    La información de posición se puede utilizar para determinar si un q-gram coincidente es realmente un "buen partido".

    Por ejemplo, si estás en busca de "foobar" con la máxima distancia de edición de 2, esto significa que sólo estás interesado en palabras donde

    2-gram "fo" exists in with position from 1 to 3 or 
    2-gram "oo" exists in with position from 2 to 4 or 
    ... and so on 
    

    cadena "barfoo" doesn' t conseguir cualquier partidos porque las posiciones de la de otro modo similar 2-gramas difieren por 3.

  2. Además, podría ser útil para u se la relación entre la distancia de edición y el recuento de q-grams coincidentes. El intution es que desde

    una cadena s ha len (s) -q + ​​1 q-gramos

    y

    una sola operación de edición puede afectar en la mayoría de Q Q-gramos,

    se puede deducir que

    cadenas s1 y s2 dentro de distancia de edición D tienen al menos max (len (s1), len (s2)) - q + 1-qk corresponde a q-grams no posicionales.

    Si estás en busca de "foobar" con una distancia de edición máximo de 2, un cadena de 7 caracteres a juego (como "fotocar") debe contener al menos dos comunes de 2 gramos.

  3. Por último, lo más obvio es a filtro por longitud. La distancia de edición entre dos cadenas está en al menos la diferencia de las longitudes de las cadenas. Por ejemplo, si su umbral es 2 y busca "foobar", "foobarbar" no puede coincidir con .

Consulte http://pages.stern.nyu.edu/~panos/publications/deb-dec2001.pdf para obtener más y algunos pseudo SQL.

2

interesante documento sobre la indexación de ADN q-gramos por lo que no tiene que escanear toda la tabla:

www.comp.nus.edu.sg/~atung/publication/qgram_edit.pdf

4

Ciertamente has visto las búsquedas de texto difuso en todas partes. Por ejemplo, escribe "stck" pero en realidad quiere decir "stack". ¿Alguna vez se preguntó cómo funciona esto?

Hay muchos algoritmos para hacer coincidencias de texto difuso, cada uno con sus propios pro y contras. Los más famosos son editar distancia y qgram. Quiero centrarme en qgrams hoy e implementar una muestra.

Básicamente qgrams son el algoritmo de coincidencia de cadenas difusas más adecuado para las bases de datos relacionales. Es bastante simple. "q" en qgram será reemplazado por un número como 2-gramo o 3-gramo o incluso 4-gramo.

2-gramo significa que cada palabra se divide en un conjunto de dos gramos de caracteres. "Pila" se dividirá en un conjunto de {"st", "ta", "ac", "ck"} o "base de datos" se dividirá en {"da", "at", "ta", "ba "," como "," se "}.

Una vez que las palabras se dividen en 2 gramos, podemos buscar en la base de datos un conjunto de valores en lugar de una cadena. Por ejemplo, si el usuario escribió mal "stck", cualquier búsqueda de "stck" no coincidirá con "stack" porque falta "a", pero el conjunto de 2 gramos {"st", "tc", "ck"} tiene 2 filas en común con el conjunto de 2 gramos de pila! Bingo encontramos una coincidencia muy cercana. No tiene nada en común con el conjunto de 2 gramos de la base de datos y solo 1 en común con el conjunto de 2 gramos de "stat", por lo que podemos sugerir fácilmente al usuario que quería escribir: primero "apilar" o segundo, "estrella" ".

Ahora impleméntelo con el servidor Sql: asuma un conjunto de datos de palabras hipotéticas. Necesita tener una relación de muchos a muchos entre 2 gramos y palabras.

CREATE TABLE Grams(twog char(2), wordId int, PRIMARY KEY (twog, wordId)) 

La tabla de gramos se debe agrupar en el primer botón y luego en la palabra Id para ver el rendimiento. Cuando consulta una palabra (por ejemplo, pila) coloca los gramos en una tabla temporal. Primero permitamos crear algunos millones de registros falsos.

--make millions of 2grams 
DECLARE @i int =0 
WHILE (@i<5000000) 
BEGIN 
-- a random 2gram 
declare @rnum1 char = CHAR(CAST(RAND()*28 AS INT)+97) 
declare @rnum2 char = CHAR(CAST(RAND()*28 AS INT)+97) 
INS... INTO Grams (twog, wordId) VALUES (@rnum1 + @rnum2, CAST(RAND()*100000 AS int)) 
END 

Ahora vamos a consulta la palabra "pila", que se romperá a: { 'st', 'ta', 'ac', 'ck'} dos gramos.

DECLARE @word TABLE(twog char(2)) -- 'stack' 
INS... INTO @word VALUES ('st'), ('ta'), ('ac'), ('ck') 

select wordId, count(*) from @word w inner join Grams g ON w.twog = g.twog 
GROUP BY wordId 

Usted debe asegurarse de que SQL Server utiliza un montón de índice agrupado busca (o loockups) para la ejecución de esta consulta.Debería ser la elección natural, pero a veces las estadísticas pueden estar dañadas o desactualizadas y SqlServer puede decidir que un escaneo completo sea más económico. Esto generalmente ocurre si no se conoce la cardinalidad de la tabla de la izquierda, por ejemplo, SqlServer puede suponer que la tabla de @word es masiva y millones de loockups van a ser más caros que una exploración de índice completa.

0

Tengo una mejora simple que no eliminará el escaneo, pero lo agilizará si usa solo 2 gramos o 3 gramos: reemplace las letras por números. La mayoría de los motores SQL funcionan mucho más rápido cuando se comparan los números.

Ejemplo: nuestra tabla fuente contiene entradas de texto en una columna. creamos una tabla temporal donde Dividimos los nombres en 2-gramos usando un

SELECT SUBSTRING (column, 1,2) as gram, 1 as position FROM sourcetable 
UNION 
SELECT SUBSTRING (column, 2,2) as gram, 2 as position FROM sourcetable 
UNION 
SELECT SUBSTRING (column, 3,2) as gram, 3 as position FROM sourcetable 

etc. 

Esto debería funcionar en un bucle donde i = 0 y j = el tamaño máximo de una entrada de fuente.

Luego, preparamos una tabla de asignación que contiene todos los posibles gramos de 2 letras e incluye una columna IDENTIDAD (1,1) llamada gram_id. Podemos ordenar los gramos por frecuencia en el diccionario de inglés y eliminar los gramos más infrecuentes (como 'kk' o 'wq') - esta clasificación puede llevar un tiempo e investigar, pero asignará los números más pequeños a los gramos más frecuentes, que Mejorará el rendimiento si podemos limitar el número de gramos a 255 porque entonces podemos usar una columna minúscula para el gram_id.

Luego reconstruimos otra tabla temporal de la primera, donde usamos el gram_id en lugar del gram. Eso se convierte en la mesa maestra. Creamos un índice en la columna gram_id y en la columna de posición.

Luego, cuando tenemos que comparar una cadena de texto a la tabla maestra, primero dividimos la cadena de texto dividiéndola en 2 gramos, luego reemplazamos los 2 gramos por su gram_id (usando la tabla de asignación) y los comparamos al de la tabla maestra

Eso hace una gran cantidad de comparaciones, pero la mayoría de ellos son enteros de 2 dígitos, lo cual es muy rápido.

Cuestiones relacionadas