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", хотя мне нужны оба результата.
Так что на самом деле у меня есть два вопроса:
возможно ли дальнейшее ускорение запроса? Моя цель-30 секунд для запроса, но я не знаю, если это реалистичный...
Как я могу получить реальный поиск подстроки с помощью 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*';
Я столкнулся с чем-то похожим на вашу проблему. Вот мое предложение попробуйте создать таблицу перевода, которая будет переводить все слова в цифры. Тогда ищите числа вместо слов.
пожалуйста, дайте мне знать, если это помогает.
Не уверен в ускорении, так как вы используете sqllite, но для поиска подстрок я сделал такие вещи, как
SET @foo_bar = 'foo bar'
SELECT * FROM table WHERE name LIKE '%' + REPLACE(@foo_bar, ' ', '%') + '%'
конечно, это возвращает только записи, которые имеют слово " foo "перед словом"bar".