Q-грамма приближенного соответствия оптимизаций - PullRequest
6 голосов
/ 21 декабря 2009

У меня есть таблица, содержащая 3 миллиона записей о людях, для которых я хочу выполнить нечеткое сопоставление, используя q-граммы (например, по фамилии). Я создал таблицу из 2 граммов, ссылающихся на это, но производительность поиска по этому объему данных невелика (около 5 минут).

У меня два вопроса: (1) Можете ли вы предложить какие-либо способы повышения производительности, чтобы избежать сканирования таблицы (т. Е. Нужно считать общие q-граммы между строкой поиска и 3 миллионами фамилий) (2) С q-граммами, если A похож на B и C похож на B, означает ли это, что C похож на A?

С уважением

Peter

Ответы [ 4 ]

6 голосов
/ 04 марта 2010

В последнее время я искал нечеткое совпадение строк, поэтому даже здесь есть риск ответить на заброшенный вопрос. Надеюсь, вы найдете это полезным.

Полагаю, вас интересуют только строки, для которых расстояние редактирования меньше заданного значения. И ваши q-граммы (или n-граммы) выглядят так

2-grams for "foobar": {"fo","oo","ob","ba","ar"}
  1. Вы можете использовать позиционные q-грамм:

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

    Позиционная информация может использоваться для определения соответствия Q-грамм действительно "хорошее совпадение".

    Например, если вы ищете "foobar" с максимальным расстоянием редактирования из 2, это означает, что вы только интересуют слова где

    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
    

    Строка "barfoo" не получает совпадает, потому что позиции в противном случае соответствие 2-грамм отличается 3.

  2. Также, это может быть полезным для использования соотношение между расстоянием редактирования и количество соответствующих q-грамм. Интуиция такова, что с

    строка s имеет len (s) -q + ​​1 q-грамм

    и

    одна операция редактирования может повлиять не более на q q-грамм,

    мы можем сделать вывод, что

    строки s1 и s2 на расстоянии редактирования d имеют по крайней мере max (len (s1), len (s2)) - q + 1-qk, совпадающие с непозиционными q-граммами.

    Если вы ищете "foobar" с максимальным расстоянием редактирования 2, соответствующее 7-символьная строка (например, «фотокар») должен содержать хотя бы два общих 2-грамма.

  3. Наконец, очевидная вещь, которую нужно сделать, это фильтр по длине . Редактирование расстояние между двумя струнами составляет наименьшая разница длин из строк. Например, если ваш порог равен 2, и вы ищете "foobar", "foobarbar" не могут очевидно совпадают.

См. http://pages.stern.nyu.edu/~panos/publications/deb-dec2001.pdf для получения дополнительной информации и некоторых псевдо-SQL.

5 голосов
/ 21 сентября 2011

Вы наверняка видели нечеткий текстовый поиск везде. Например, вы набираете «stck», но на самом деле вы имеете в виду «стек»! Когда-нибудь задумывались, как это работает?

Существует множество алгоритмов для нечеткого сопоставления текста, каждый со своими плюсами и минусами. Самые известные из них - это расстояние редактирования и qgram. Сегодня я хочу сосредоточиться на qgrams и реализовать пример.

В основном qgrams являются наиболее подходящим алгоритмом нечеткого сопоставления строк для реляционных баз данных. Это довольно просто. «q» в qgram будет заменено на число, такое как 2 грамма или 3 грамма или даже 4 грамма.

2 грамма означает, что каждое слово разбито на набор из двух символов. «Стек» будет разбит на набор {«st», «ta», «ac», «ck»} или «база данных» будет разбит на {«da», «at», «ta», «ba». », "как", "с"}.

Как только слова разбиты на 2 грамма, мы можем искать в базе данных набор значений вместо одной строки. Например, если пользователь неправильно набрал «stck», любой поиск по «stck» не будет соответствовать «стеку», потому что «a» отсутствует, но набор из 2 граммов {«st», «tc», «ck»} имеет 2 строки общего с 2-граммовым набором стека! Бинго мы нашли довольно близкий матч. Он не имеет ничего общего с 2-граммовым набором базы данных и только 1 имеет общие черты с 2-граммовым набором «stat», поэтому мы можем легко предложить пользователю, что он хотел напечатать: первый «стек» или второй, «звезда» ».

Теперь давайте реализуем это с помощью Sql Server: предположим, гипотетический набор данных слов. Вы должны иметь много-много отношений между 2 граммами и словами.

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

Таблица граммов должна быть сгруппирована по первой ветке, а затем по слову WordId для производительности. Когда вы запрашиваете слово (например, стек), вы помещаете граммы во временную таблицу. Сначала давайте создадим несколько миллионов фиктивных записей.

--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

Теперь давайте запросим слово «стек», которое будет разбито на: {'st', 'ta', 'ac', 'ck'} два грамма.

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

Вы должны убедиться, что Sql Server использует несколько запросов кластеризованного индекса (или циклов) для выполнения этого запроса. Это должен быть естественный выбор, но иногда статистика может быть повреждена или устарела, и SqlServer может решить, что полное сканирование дешевле. Обычно это происходит, если он не знает мощности левой таблицы, например, SqlServer может предположить, что таблица @word огромна и миллионы циклов будут дороже, чем полное сканирование индекса.

2 голосов
/ 05 августа 2010

Интересная статья об индексировании q-граммов ДНК, поэтому вам не нужно сканировать всю таблицу:

www.comp.nus.edu.sg / ~ atung / публикации / qgram_edit.pdf

0 голосов
/ 08 января 2018

У меня есть простое улучшение, которое не устранит сканирование, но ускорит его, если вы используете только 2 грамма или 3 грамма: замените буквы цифрами. Большинство механизмов SQL работают намного быстрее при сравнении чисел.

Пример: наша исходная таблица содержит текстовые записи в одном столбце. Мы создаем временную таблицу, в которой мы разбиваем имена на 2 грамма, используя

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. 

Это должно выполняться в цикле, где i = 0 и j = максимальный размер исходной записи.

Затем мы готовим таблицу сопоставления, которая содержит все возможные двухбуквенные граммы и включает столбец IDENTITY (1,1) с именем gram_id. Мы можем отсортировать граммы по частоте в словаре английского языка и исключить наиболее редкие граммы (например, «kk» или «wq») - эта сортировка может занять некоторое время и исследовать, но она назначит наименьшее число наиболее частым граммам, что затем повысит производительность, если мы сможем ограничить количество граммов до 255, потому что тогда мы можем использовать столбец tinyint для gram_id.

Затем мы перестраиваем другую временную таблицу из первой, где вместо грамм мы используем gram_id. Это становится главной таблицей. Мы создаем индекс для столбца gram_id и столбца позиции.

Затем, когда нам нужно сравнить текстовую строку с основной таблицей, мы сначала разбиваем текстовую строку на 2 грамма, затем заменяем 2 грамма на их gram_id (используя таблицу сопоставления) и сравниваем их с один из мастер-таблицы

Это делает много сравнений, но большинство из них представляют собой двузначные целые числа, что очень быстро.

...