Индексы не работают на sqlite таблице - PullRequest
2 голосов
/ 02 марта 2012

Я использую индексы по столбцам, по которым я выполняю поиск. Индексы создаются так:

CREATE INDEX index1 on <TABLE>(<col1> COLLATE NOCASE ASC)
CREATE INDEX index2 on <TABLE>(<col2> COLLATE NOCASE ASC)
CREATE INDEX index3 on <TABLE>(<col3> COLLATE NOCASE ASC)

Теперь запрос на выборку для поиска записей выглядит так:

select <col1> from <TABLE> where <col1> like '%monit%' AND <col2> like '%84%'   GROUP BY <col1> limit 0,501;

Когда я запускаю EXPLAIN QUERY PLAN в моей базе данных sqlite следующим образом:

EXPLAIN QUERY PLAN select <col1> from <TABLE> where <col1> like '%monit%' AND <col2> like '%84%'   GROUP BY <col1> limit 0,501;

Возвращает вывод в виде:

0 | 0 | 0 | ИНДЕКС СКАНИРОВАНИЯ ТАБЛИЦЫ ИСПОЛЬЗОВАНИЯ (~ 250000 строк)

и когда я отбрасываю индекс, выводится этот EXPLAIN QUERY PLAN:

0 | 0 | 0 | ТАБЛИЦА СКАНИРОВАНИЯ (~ 250000 строк) 0 | 0 | 0 | ИСПОЛЬЗУЙТЕ B-ДЕРЕВО ТЕМП ДЛЯ ГРУППЫ ПО

Разве число сканируемых строк (~ 250000 строк) не должно было быть меньше, если при поиске в таблице использовался индекс ???

Полагаю, проблема здесь в ключевом слове LIKE, потому что я где-то читал, что ключевое слово LIKE обнуляет использование индексов if ... Вот ссылка

РЕДАКТИРОВАТЬ: чтобы индексы работали с запросом, использующим LIKE, правая часть LIKE должна быть строковым литералом, который не начинается с символа подстановки. Итак, в приведенном выше запросе я попытался использовать параметр поиска в «как» без «%» в начале:

EXPLAIN QUERY PLAN select <col1> from <TABLE> where <col1> like 'monit%' AND <col2> like '84%'   GROUP BY <col1> limit 0,501;

и вывод, который я получил, был таким:

0 | 0 | 0 | SEARCH TABLE partnumber ИСПОЛЬЗОВАНИЕ ИНДЕКСА model_index_partnumber (модель>? AND модель

Итак, вы видите. Количество строк, в которых выполняется поиск (а не сканирование), составляет (~ 15625 строк). Но проблема в том, что я не могу покончить с джокером% в начале. Любой, пожалуйста, предложите мне альтернативный способ достижения того же ....

EDIT: Я пытался использовать FTS3 из терминала, но когда я набрал этот запрос:

CREATE VIRTUAL TABLE <tbl> USING FTS3 (<col_list>);

Ошибка его выдачи: Ошибка: нет такого модуля: FTS3

Кто-то, пожалуйста, помогите мне включить FTS3 из терминала, а также XCode (нужны шаги, которые я должен выполнить для обеих задач).

Я использую sqlcipher и уже выполнил это с терминала:

CFLAGS="-DSQLITE_ENABLE_FTS3=1" ./configure 

EDIT:

Пожалуйста, посетите вопрос Таблица sqlite занимает время для извлечения записей в LIKE-запросе , опубликованном мной

EDIT:

Привет, все, я добился определенного успеха. Я изменил свой запрос select, чтобы он выглядел так:

select distinct description collate nocase as description from partnumber where rowid BETWEEN 1 AND (select max(rowid) from partnumber) AND description like '%a%' order by description;

И, Бинго, время поиска было как никогда раньше. Но проблема сейчас в том, что когда я выполняю команду EXPLAIN QUERY PLAN следующим образом, она показывает, что я использую B-Tree для разных, которые я не хочу использовать.

explain query plan select distinct description collate nocase as description from partnumber where rowid BETWEEN 1 AND (select max(rowid) from partnumber) AND description like '%a%' order by description;

Выход:

0|0|0|SEARCH TABLE partnumber USING INTEGER PRIMARY KEY (rowid>? AND rowid<?) (~15625 rows)
0|0|0|EXECUTE SCALAR SUBQUERY 1
1|0|0|SEARCH TABLE partnumber USING INTEGER PRIMARY KEY (~1 rows)
0|0|0|USE TEMP B-TREE FOR DISTINCT

Ответы [ 3 ]

3 голосов
/ 02 марта 2012

Пара других опций ...

Полнотекстовые индексы:

http://sqlite.org/fts3.html

Самый распространенный (и эффективный) способ описаниятекстовый поиск - это «то, что Google, Yahoo и Altavista делают с документами, размещенными в World Wide Web».

SELECT count(*) FROM enrondata1 WHERE content MATCH 'linux';  /* 0.03 seconds */
SELECT count(*) FROM enrondata2 WHERE content LIKE '%linux%'; /* 22.5 seconds */ 

Разрыв слов:

Если вы ищете слова (или слова, начинающиеся с), вы можете разбивать текстовые объекты на слова самостоятельно и сохранять свои собственные таблицы проиндексированных слов.Но даже тогда вы сможете использовать только слово типа 'monit%', чтобы получить совпадения типа "монитор"

Если возможно, используйте полный текст - кода будет намного меньше.Но, если по какой-то причине это невозможно, вы можете вернуться к своим собственным таблицам разбиения по словам, но это ограниченное количество слов начинается с того, чтобы избежать сканирования.(лучше, чем весь текстовый блок начинается с).

Имейте в виду, что в sqlite, поставляемом с iOS, не включен Full Text.Вы можете обойти это.Инструкции по этому вопросу можно найти по адресу:

http://longweekendmobile.com/2010/06/16/sqlite-full-text-search-for-iphone-ipadyour-own-sqlite-for-iphone-and-ipad/

Полный список документов по созданию и запросу полнотекстовых таблиц приведен здесь: http://sqlite.org/fts3.html

Чтобы получить FTS3 длятакже работают с терминала, см .:

Компиляция интерфейса командной строки @ http://www.sqlite.org/howtocompile.html

sqlite3 с использованием таблицы создания fts3 в моем терминале Mac и как использовать ее в проекте iphone xcode?

2 голосов
/ 07 марта 2012

Это довольно просто. Вы говорите SQLITE проверить каждую запись в таблице. Это быстрее сделать без использования индекса, потому что использование индекса потребует дополнительного ввода-вывода. И индекс используется, когда вы хотите исследовать подмножество записей в таблице, где дополнительная IO использования индекса окупается из-за того, что нет необходимости проверять каждую запись в таблице.

Когда вы говорите LIKE"% что-то", это означает all records with anything at all at the beginning of the field, followed by something. Единственный способ сделать это - изучить каждую запись. Обратите внимание, что индексы по-прежнему должны использоваться, если вы используете только LIKE"что-то%", потому что в этом случае SQLITE может использовать индекс, чтобы найти подмножество записей, начинающихся с "что-то". В старые времена, когда базы данных были не такими умными, мы писали это так, чтобы обеспечить использование индекса. SELECT * WHERE col1 >= "something" AND col1 < "somethinh", обратите внимание на намеренное неправильное написание чего-либо во втором условии.

Если вы можете, лучше избегать использования % в начале условия LIKE. В некоторых случаях вы можете изменить свою схему так, чтобы данные сохранялись в двух столбцах, а не в одном. Затем вы используете поиск LIKE"что-то%" во втором из двух столбцов. Конечно, это зависит от правильности структурированных данных.

Но даже если разделение на два столбца невозможно, может оказаться возможным разделить и завоевать данные другим способом. Например, вы можете разделить поля поиска на слова и индексировать каждое слово в одном столбце в другой таблице поиска. Таким образом, «искать что-то или другое» становится списком записей, где «что-то» является точным соответствием записи в таблице поиска. Не нравится, как. Затем вы получите идентификатор записи для извлечения исходной записи. Это одна из вещей, которые SOLR делает внутренне, поэтому, если вы должны придерживаться SQLITE и никак не можете использовать SOLR или LUCENE, тогда вы всегда можете прочитать о том, как они создают инвертированные индексы и делают то же самое самостоятельно в своей базе данных SQLITE.

Помните, что LIKE"%thing%" должен проверять каждую запись, но если вы можете сначала выбрать подмножество данных, а затем применить поиск LIKE , он запустится намного быстрее Заполнение кеша будет иметь тот же эффект, что и ваши эксперименты с DISTINCT. Может быть, все, что вам нужно сделать, это увеличить кэш, чтобы получить приемлемое время поиска. Первый поиск все еще будет медленным, но люди часто прощают проблемы, которые исчезают, когда вы повторяете его.

Когда вы используете произвольные символы подстановки, подобные этим, вы очень близко подходите к полнотекстовой поисковой системе, такой как SOLR. Они работают путем индексации данных на 100% в оперативной памяти. С SQLITE вы можете сделать что-то подобное, создав вторую базу данных в памяти, считав все данные из таблиц дисков в базу данных в памяти, а затем используя базу данных в памяти для поиска с подстановочными знаками. Вы по-прежнему имели бы полные таблицы с запросами, такими как LIKE "%monit%", однако это сканирование происходит в оперативной памяти, где это не занимает много времени. Вам не нужно импортировать все ваши данные в оперативную память, только те части, где вам нужно выполнить поиск «%thing%», потому что SQLITE может выполнять соединения между базами данных. SQLITE упрощает создание базы данных в памяти, а команды ATTACH DATABASE и DETACH DATABASE упрощают подключение второй базы данных к вашему приложению. В этом вопросе приведен пример кода для IOS Могут ли приложения iPhone sqlite подключаться к другим базам данных?

Не уверен, почему вам не нравится EXPLAIN с использованием B-деревьев, поскольку B-дерево, вероятно, является самой быстрой из возможных структур поиска, доступных, когда ваши данные должны быть прочитаны из файловой системы.

1 голос
/ 07 марта 2012

У меня есть книга MySQL, в которой предлагается REVERSE() текст (и, если позволяет ваше приложение, сохраните в столбце).Затем найдите перевернутый текст, используя LIKE(REVERSE('%something')).

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