Оптимизация SQL-запроса на SQLite3 с помощью индексов

Я пытаюсь оптимизировать SQL-запрос, создавая индексы с наилучшими характеристиками.

определение

CREATE TABLE Mots (
  numero            INTEGER NOT NULL, 
  fk_dictionnaires integer(5) NOT NULL, 
  mot              varchar(50) NOT NULL, 
  ponderation      integer(20) NOT NULL,
  drapeau varchar(1) NOT NULL,
  CONSTRAINT pk_mots PRIMARY KEY(numero),
  CONSTRAINT uk_dico_mot_mots UNIQUE(fk_dictionnaires, mot),
  CONSTRAINT fk_mots_dictionnaires FOREIGN KEY(fk_dictionnaires) REFERENCES Dictionnaires(numero)
  );

индексы определение

CREATE INDEX idx_dictionnaires ON mots(fk_dictionnaires DESC);
CREATE INDEX idx_mots_ponderation ON mots(ponderation);
CREATE UNIQUE INDEX idx_mots_unique ON mots(fk_dictionnaires, mot);

SQL запрос :

SELECT numero, mot, ponderation, drapeau 
FROM mots 
WHERE mot LIKE 'ar%' 
   AND fk_dictionnaires=1 
   AND LENGTH(mot)>=4 
   ORDER BY ponderation DESC 
LIMIT 5;

План Запроса

0|0|0|SEARCH TABLE mots USING INDEX idx_dictionnaires (fk_dictionnaires=?) (~2 rows)
0|0|0|USE TEMP B-TREE FOR ORDER BY

индексы не используются и запрос длится (по .таймер) :

CPU Time: user 0.078001 sys 0.015600

однако, когда я удалил fk_dictionnaires=1. Мои индексы правильно используются, и представления вокруг 0.000000-0.01 XXXXXX сек

0|0|0|SCAN TABLE mots USING INDEX idx_mots_ponderation (~250000 rows)

я узнал некоторые аналогичные вопросы о stackoverflow, но не anwser помочь мне.

как я могу улучшить производительность с помощью индексов или / и путем изменения SQL-запроса? Спасибо заранее.

1 ответов


SQLite, кажется, думает, что idx_dictionnaires индекс очень разрежен и делает вывод, что если он сканирует с помощью idx_dictionnaires, ему нужно будет только изучить пару строк. Однако результаты производительности, которые вы цитируете, предполагают, что он должен рассматривать больше, чем просто пару строк. Во-первых, почему бы вам не попробовать ANALYZE mots, поэтому SQLite, которая будет иметь актуальную информацию о мощности каждого индекса?

вот что еще может помочь, из SQLite документация:


условия предложения WHERE могут быть вручную дисквалифицированы для использования с индексами путем добавления унарного оператора + к имени столбца. Унарный + является no-op и не замедлит оценку теста, указанного термином. Но это предотвратит ограничение индекса термином. Так, в приведенном выше примере, если запрос был переписан так:

SELECT z FROM ex2 WHERE +x=5 AND y=6;

оператор + в столбце x предотвратит ограничение индекса этим термином. Это заставит использовать индекс ex2i2.

обратите внимание, что унарный оператор + также удаляет сродство типов из выражения, и в некоторых случаях это может вызвать тонкие изменения в значении выражения. В приведенном выше примере, если столбец x имеет сродство текста, то сравнение "x=5" будет выполнено как текст. Но оператор + удаляет сродство. Таким образом, сравнение "+x=5" будет сравнивать текст в столбце x с числовым значением 5 и всегда будет ложный.


если ANALYZE mots недостаточно, чтобы помочь SQLite выбрать лучший индекс для использования, вы можете использовать эту функцию, чтобы заставить его использовать нужный индекс.

вы также можете попробовать составные индексы - похоже, вы уже определили один на fk_dictionnaires,mot, но SQLite не использует его. Для" быстрого " запроса SQLite, казалось, предпочитал использовать индекс на ponderation, чтобы избежать сортировки строк в конце запроса. Если вы добавите индекс на fk_dictionnaires,ponderation DESC, и SQLite фактически использует его, это можно выбрать строки, которые соответствуют fk_dictionnaires=1 без сканирования таблицы и избегайте сортировки в конце.


POSTSCRIPT: составной индекс, который я предложил выше, "исправил" проблему производительности OP, но он также спросил, как и почему он работает. @AGeiser, я использую краткую иллюстрацию, чтобы попытаться помочь вам интуитивно понять индексы БД:

представьте, что вам нужно найти всех людей в вашем городе, фамилии которых начинаются с "а". У вас есть каталог всех имен, но они расположены в случайном порядке. Чем вы занимаетесь? У вас нет выбора, кроме как прочитать весь каталог и выбрать те, которые начинаются с "A". Похоже, много работы, да? (Это похоже на таблицу БД без индексов.)

путь быстрее. (Это похоже на таблицу БД с индексом; в этом случае назовите ее индексом на last_name,first_name.)

теперь что, если вы хотите, чтобы все люди, чьи имена начинаются с "А", но в случае, если 2 человека имеют одно и то же имя, вы хотите, чтобы они были заказаны по почтовому индексу? Даже если вы быстро получите необходимые имена, используя " телефонную книгу "(т. е. индекс last_name,first_name), вам все равно придется сортировать все вручную... так что это снова начинает звучать как много работы. Что может сделать эту работу действительно легкой?

потребовалась бы еще одна "телефонная книга" -- но та, в которой записи заказываются сначала по имени, а затем по почтовому коду. С "телефонной книге" можно быстро выбрать диапазон записей, которые вам нужны, и вам даже не нужно сортировать их-они уже будут в нужном порядке. (Это индекс на last_name,first_name,postal_code.)

я думаю, что это иллюстрация должна прояснить, как индексы могут помочь выбрать запросы, не только уменьшая количество строк, которые должны быть рассмотрены, но и (потенциально) устраняя необходимость отдельной фазы "сортировки" после того, как будут найдены необходимые строки. Надеюсь, это также дает понять, что составной индекс на a,b полностью отличается от одного на b,a. Я мог бы привести еще несколько примеров из "телефонной книги", но этот ответ стал бы таким длинным, что стал бы больше похож на сообщение в блоге. Построить ваш интуиция, по которой индексы могут принести пользу запросу, я рекомендую книгу О'Рейли " SQL Antipatterns "(особенно Главу 13"Index Shotgun").