2013-03-20 4 views
1

(Возможно, дубликат, но я могу найти только вопросы и решения с РЕГИСТРИРУЙТЕСЬ [3] и это не вариант.)Какой подзапрос работает быстрее?

У меня есть две таблицы. Оба очень тонкие (несколько столбцов) и очень длинны (много строк). Одна из них - таблица данных (articles), а другая - таблица ACL (acl).

Я хочу показать только те статьи, к которым у меня есть доступ, через acl.some_id. Какой подзапрос работает быстрее?

[1] 
SELECT a.title 
FROM articles a 
WHERE 0 < (
    SELECT COUNT(1) 
    FROM acl 
    WHERE article_id = a.id AND some_id IN (1, 2, 3) 
) 

или

[2] 
SELECT a.title 
FROM articles a 
WHERE a.id IN (
    SELECT article_id 
    FROM acl WHERE some_id IN (1, 2, 3) 
) 

Мой разум сказал бы второй, потому что подзапрос может быть повторно использована для всех потенциально совпадающей строки, поэтому будет выполняться только один раз (хотя результирующий набор будет очень большой), тогда как подзапрос в первом должен будет проверять для КАЖДОЙ потенциально подходящей строки.

Там есть третий путь, но это не вариант, поскольку она будет дублировать строки (и GROUP BY не является решением, потому что мне нужно COUNT для чего-то еще позже (и DISTINCT никогда не решение!)):

[3] 
SELECT a.title 
FROM articles a 
JOIN acl 
    ON acl.article_id = a.id 
WHERE acl.some_id IN (1, 2, 3) 

Поскольку article_id X существует N раз в acl, было бы вернуть эту строку 0 - N раз вместо 0 - 1.

Там также четвертый путь: EXISTS. Благодаря ypercube.

Похожие:

+5

Вы написали код ... ** ПОПРОБУЙТЕ И И НАЙТИ! ** –

+0

Попытка раз или 15 раз на самом деле ничего не значит. Я хочу, почему. И таблицы еще не длинны =), поэтому время выполнения будет очень, очень коротким. – Rudie

+3

У mysql есть все инструменты, необходимые для ответа. Посмотрите план выполнения для каждого запроса. Просмотрите время выполнения. Вы можете сделать это. –

ответ

5

Я бы сказал [2], тоже, но MySQL имеет некоторые слепые пятна в оптимизации IN подзапросов, по крайней мере до 5,5. В версии (недавно выпущенной версии) версии 5.6 есть несколько улучшений оптимизатора запросов. Вы можете прочитать о (semijoins и IN подзапросах) в документах MySQL: MySQL 5.6: Optimizing Subqueries with Semi-Join Transformations.

Существует также несколько улучшений оптимизатора в MariaDB (версии 5.3 и 5.5), а некоторые из них связаны с такими запросами. Вы можете прочитать о них в своих документах: MariaDB 5.3: Semi-join subquery optimizations.

Вы также можете попробовать версию EXISTS, особенно если вы используете 5.5 или более раннюю версию:

-- [4] 
SELECT id 
FROM articles AS a 
WHERE EXISTS (
    SELECT * 
    FROM acl 
    WHERE acl.some_id IN (1, 2, 3) 
    AND acl.article_id = a.id 
) ; 

Я думаю, что индекс по (article_id, some_id) будет полезно здесь - или, возможно, наоборот один, он не больно попробовать оба.


Если есть внешний ключ из acl (article_id) REFERENCES article (id), что вы можете доверять, и вам нужно только идентификаторы статей, вы можете также получить данные только из одной таблицы:

SELECT DISTINCT article_id 
FROM acl 
WHERE acl.some_id IN (1, 2, 3) ; 

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

+0

Ах да СУЩЕСТВУЕТ, это тоже вариант. Похоже, это было сделано, чтобы сделать именно это. (У меня есть эти индексы кстати.) – Rudie

+0

СУДЕБНО, я думаю. Гораздо быстрее, чем IN + COUNT, очевидно: http://www.jortk.nl/2008/07/exists-much-faster-then-in-in-mysql/ (хотя это очень старый). Вероятно, потому что EXISTS останавливается/возвращается после обнаружения 1 записи. – Rudie

+0

Последний запрос (только возврат идентификаторов товаров) недостаточен, потому что я хочу гораздо больше, чем идентификатор статьи. Сожалею. Не очевидно. – Rudie

Смежные вопросы