SQLite: эффективный поиск подстроки в большой таблице

Я разрабатываю приложение для Android, которое должно выполнять поиск подстроки в большой таблице (около 500 000 записей с именами улиц и местоположений, поэтому всего несколько слов на запись).

CREATE TABLE Elements (elementID INTEGER, type INTEGER, name TEXT, data BLOB)

обратите внимание, что только 20% всех записей содержат строки в графе "Наименование".

выполнив следующий запрос почти 2 минуты:

SELECT elementID, name FROM Elements WHERE name LIKE %foo%

теперь я попытался использовать FTS3 для ускорения запроса. Это было довольно успешно, время запроса уменьшилось до 1 минута (удивительно, что размер файла базы данных увеличился всего на 5%, что также неплохо для моей цели).

проблема в том, что FTS3, похоже, не поддерживает поиск подстроки, т. е. если я хочу найти "бар" в "foo bar" и "foobar", я получаю только "foo bar", хотя мне нужны оба результата.

Так что на самом деле у меня есть два вопроса:

  1. возможно ли дальнейшее ускорение запроса? Моя цель-30 секунд для запроса, но я не знаю, если это реалистичный...

  2. Как я могу получить реальный поиск подстроки с помощью FTS3?

4 ответов


Решение 1: Если вы можете сделать каждый символ в своей базе данных отдельным словом, вы можете использовать поисковые фразы поиск подстроки.

например, предположим, что "my_table" содержит один столбец "person":

person
------
John Doe
Jane Doe

вы можете изменить его на

person
------
J o h n D o e
J a n e D o e

для поиска подстроки " ohn " используйте фразу query:

SELECT * FROM my_table WHERE person MATCH '"o h n"'

остерегайтесь, что " Джонд "будет соответствовать" Джон Доу", что может быть нежелательно. Чтобы исправить это, измените пробел в исходной строке в нечто другое.

например, вы можете заменить символ пробела на"$":

person
------
J o h n $ D o e
J a n e $ D o e

решение 2: Следуя идее решения 1, Вы можете сделать каждый символ отдельным словом с пользовательским токенизатором и использовать запросы фраз для запроса подстрок.

преимущество перед решением 1 заключается в том, что вам не нужно добавлять пробелы в данные, что может излишне увеличить размер база данных.

недостатком является то, что вам нужно реализовать пользовательский токенизатор. К счастью, у меня есть один готов для вас. Код находится на C, поэтому вам нужно выяснить, как интегрировать его с вашим Java-кодом.


вы должны добавить индекс к name столбец в вашей базе данных, который должен значительно ускорить запрос.

Я считаю, что SQLite3 поддерживает соответствие подстрок следующим образом:

SELECT * FROM Elements WHERE name MATCH '*foo*';

http://www.sqlite.org/fts3.html#section_3


Я столкнулся с чем-то похожим на вашу проблему. Вот мое предложение попробуйте создать таблицу перевода, которая будет переводить все слова в цифры. Тогда ищите числа вместо слов.

пожалуйста, дайте мне знать, если это помогает.


Не уверен в ускорении, так как вы используете sqllite, но для поиска подстрок я сделал такие вещи, как

SET @foo_bar = 'foo bar'
SELECT * FROM table WHERE name LIKE '%' + REPLACE(@foo_bar, ' ', '%') + '%'

конечно, это возвращает только записи, которые имеют слово " foo "перед словом"bar".