Я изучаю SQL (используя SQLite 3 и его инструмент командной строки sqlite3), и я заметил, что могу делать некоторые вещи несколькими способами, и иногда не ясно, какой из них лучше. Вот три запроса, которые выполняют одно и то же: одно выполняется через intersect
, другое через inner join
и distinct
, последнее похожее на второе, но включает фильтрацию через where
. (Первая была написана автором книги, которую я читаю, а остальные я написал сам.)
Вопрос в том, , какой из этих запросов лучше и почему? И, в более общем смысле, , как я могу узнать, когда один запрос лучше другого? Есть ли какие-то рекомендации, которые я пропустил, или, возможно, я должен изучить внутренние элементы SQLite, несмотря на декларативный характер SQL?
(В следующем примере есть таблицы, которые описывают имена продуктов, упомянутые в некоторых сериалах. Foods_episodes - это таблица ссылок "многие-ко-многим", в то время как другие описывают имена продуктов и имена эпизодов вместе с номером сезона. разыгрываются десятки лучших продуктов (основанные на количестве их выступлений во всех сериях), а не только лучшие продукты в сезонах 3..5)
-- task
-- find the all-time top ten foods that appear in seasons 3 through 5
-- schema
-- CREATE TABLE episodes (
-- id integer primary key,
-- season int,
-- name text );
-- CREATE TABLE foods(
-- id integer primary key,
-- name text );
-- CREATE TABLE foods_episodes(
-- food_id integer,
-- episode_id integer );
select f.* from foods f
inner join
(select food_id, count(food_id) as count
from foods_episodes
group by food_id
order by count(food_id) desc limit 10) top_foods
on f.id=top_foods.food_id
intersect
select f.* from foods f
inner join foods_episodes fe on f.id = fe.food_id
inner join episodes e on fe.episode_id = e.id
where
e.season between 3 and 5
order by
f.name;
select
distinct f.*
from
foods_episodes as fe
inner join episodes as e on e.id = fe.episode_id
inner join foods as f on fe.food_id = f.id
inner join (select food_id from foods_episodes
group by food_id order by count(*) desc limit 10) as lol
on lol.food_id = fe.food_id
where
e.season between 3 and 5
order by
f.name;
select
distinct f.*
from
foods_episodes as fe
inner join episodes as e on e.id = fe.episode_id
inner join foods as f on fe.food_id = f.id
where
fe.food_id in (select food_id from foods_episodes
group by food_id order by count(*) desc limit 10)
and e.season between 3 and 5
order by
f.name;
-- output (same for these thee):
-- id name
-- ---------- ----------
-- 4 Bear Claws
-- 146 Decaf Capp
-- 153 Hennigen's
-- 55 Kasha
-- 94 Ketchup
-- 164 Naya Water
-- 317 Pizza
-- CPU Time: user 0.000000 sys 0.000000
Подобно MySQL, похоже, что SQLlite имеет команду EXPLAIN. Подготовьте свой выбор с помощью ключевого слова EXPLAIN, и он вернет информацию о запросе, включая количество проверенных строк и используемые индексы.
http://www.sqlite.org/lang_explain.html
Запустив EXPLAIN для различных выборок, вы можете определить, какие запросы (и подзапросы) более эффективны, чем другие.
И вот общий обзор планировщика запросов SQLlite и оптимизации: http://sqlite.org/optoverview.html
SQLlite3 также поддерживает функцию обратного вызова для отслеживания запросов. Вы должны его реализовать, но: http://www.sqlite.org/c3ref/profile.html
Как правило, существует более одного способа решения проблемы. Если вы получаете правильные ответы, единственный вопрос заключается в том, нужно ли улучшить процесс / script/, или если он хорошо работает сейчас.
В SQL обычно может быть "лучший" способ, но обычно это не цель найти канонический лучший способ сделать что-то - вы хотите, чтобы он эффективно сочетал ваши потребности с программой и ваше время. может потратить месяцы на оптимизацию процесса, но если этот процесс используется только еженедельно, и это займет всего 5 минут, сокращение его до 4 минут не очень помогает.
Странно переходить из контекста, где есть правильные ответы (например, школа), в контексте, где цель состоит в том, чтобы сделать что-то хорошо, и хорошо работает на козырях, потому что есть временные ограничения. Это то, что мне понадобилось, чтобы оценить, но я не уверен, что есть лучший ответ. Надеюсь, что перспектива немного помогает!