У меня есть база данных sqlite с двумя таблицами, каждая из которых содержит 50000 строк, содержащих имена (фальшивых) людей. Я построил простой запрос, чтобы узнать, сколько существует имен (имя, отчество, фамилия), которые являются общими для обеих таблиц:
select count(*) from fakenames_uk inner join fakenames_usa on fakenames_uk.givenname=fakenames_usa.givenname and fakenames_uk.surname=fakenames_usa.surname and fakenames_uk.middleinitial=fakenames_usa.middleinitial;
Когда нет индексов, кроме первичных ключей (не относящихся к этому запросу), он выполняется быстро:
[james@marlon Downloads] $ time sqlite3 generic_data_no_indexes.sqlite "select count(*) from fakenames_uk inner join fakenames_usa on fakenames_uk.givenname=fakenames_usa.givenname and fakenames_uk.surname=fakenames_usa.surname and fakenames_uk.middleinitial=fakenames_usa.middleinitial;"
131
real 0m0.115s
user 0m0.111s
sys 0m0.004s
Но если я добавлю индексы к трем столбцам в каждой таблице (всего шесть индексов):
CREATE INDEX `idx_uk_givenname` ON `fakenames_uk` (`givenname` )
//etc.
потом он работает мучительно медленно
[james@marlon Downloads] $ time sqlite3 generic_data.sqlite "select count(*) from fakenames_uk inner join fakenames_usa on fakenames_uk.givenname=fakenames_usa.givenname and fakenames_uk.surname=fakenames_usa.surname and fakenames_uk.middleinitial=fakenames_usa.middleinitial;"
131
real 1m43.102s
user 0m52.397s
sys 0m50.696s
Есть ли какая-то рифма или причина для этого?
Вот результат EXPLAIN QUERY PLAN
для версии без индексов:
0|0|0|SCAN TABLE fakenames_uk
0|1|1|SEARCH TABLE fakenames_usa USING AUTOMATIC COVERING INDEX (middleinitial=? AND surname=? AND givenname=?)
Это с индексами:
0|0|0|SCAN TABLE fakenames_uk
0|1|1|SEARCH TABLE fakenames_usa USING INDEX idx_us_middleinitial (middleinitial=?)
performance
index
optimization
sqlite
count
chiastic-безопасности
источник
источник
middleinitial
,surname
иgivenname
)?SELECT c FROM t WHERE a=1 AND b=2
индексt(a,b,c)
покрывает, ноt(a,b)
нет. Преимущество покрытия индексов состоит в том, что весь результат запроса может быть извлечен непосредственно из индекса, тогда как непокрытые индексы быстро находят соответствующие строки, но для выбора значений все равно необходимо обращаться к данным основной таблицы.Ответы:
В SQLite объединения выполняются как объединения вложенных циклов, т. Е. База данных проходит через одну таблицу, и для каждой строки выполняется поиск соответствующих строк из другой таблицы.
Если есть индекс, база данных может быстро найти любые совпадения в индексе, а затем перейти к соответствующей строке таблицы, чтобы получить значения любых других необходимых столбцов.
В этом случае есть три возможных индекса. Без какой-либо статистической информации (которая была бы создана при запуске ANALYZE ), база данных выбирает наименьшую, чтобы уменьшить количество операций ввода-вывода. Однако
middleinitial
индекс бесполезен, потому что он не сильно уменьшает количество строк таблицы, которые необходимо извлечь; и дополнительный шаг по индексу фактически увеличивает необходимый ввод-вывод, потому что строки таблицы больше не читаются по порядку, а случайным образом.Если индекс отсутствует, для поиска совпадающих строк потребуется полное сканирование второй таблицы для каждой строки первой таблицы. Это было бы так плохо, что, по оценкам базы данных, стоит создать, а затем удалить временный индекс только для этого запроса. Этот временный («АВТОМАТИЧЕСКИЙ») индекс создается для всех полей, используемых для поиска. Операция COUNT (*) не нуждается в значениях из каких-либо других столбцов, поэтому этот индекс является индексом покрытия , что означает, что нет необходимости на самом деле искать строку таблицы, соответствующую записи индекса, что экономит еще больше I / вывода.
Чтобы ускорить этот запрос, создайте этот индекс постоянно, чтобы больше не нужно было создавать временный:
Индекс на
surname
больше не нужен, так как трехколонный индекс можно использовать для любых поисков в этом столбце.Индекс
givenname
может быть полезен, если вы будете выполнять поиск только по этому столбцу.Индекс на
middleinitial
всегда бесполезен: запрос, который ищет одно из 26 возможных значений, выполняется быстрее, если он просто просматривает всю таблицу.источник