Производительность вариантов использования SQL "EXISTS"
Есть ли разница в производительности следующих трех операторов SQL?
SELECT * FROM tableA WHERE EXISTS (SELECT * FROM tableB WHERE tableA.x = tableB.y)
SELECT * FROM tableA WHERE EXISTS (SELECT y FROM tableB WHERE tableA.x = tableB.y)
SELECT * FROM tableA WHERE EXISTS (SELECT 1 FROM tableB WHERE tableA.x = tableB.y)
Все они должны работать и возвращать один и тот же набор результатов. Но имеет ли значение, если внутренний SELECT выбирает все поля таблицыB, одно поле или просто константу?
Есть ли какая-нибудь лучшая практика, когда все утверждения ведут себя одинаково?
Ответы
Ответ 1
Истина о предложении EXISTS заключается в том, что предложение SELECT не оценивается в предложении EXISTS - вы можете попробовать:
SELECT *
FROM tableA
WHERE EXISTS (SELECT 1/0
FROM tableB
WHERE tableA.x = tableB.y)
... и должен ожидать деления на нулевую ошибку, но вы не будете, потому что он не оценивается. Вот почему моя привычка указывать NULL в EXISTS, чтобы продемонстрировать, что SELECT можно игнорировать:
SELECT *
FROM tableA
WHERE EXISTS (SELECT NULL
FROM tableB
WHERE tableA.x = tableB.y)
Все, что имеет значение в предложении EXISTS, - это предложения FROM и вне - WHERE, GROUP BY, HAVING и т.д.
Этот вопрос не был отмечен с учетом базы данных, и это должно происходить из-за того, что поставщики обрабатывают вещи по-другому - поэтому проверяйте и проверяйте планы объяснения/исполнения для подтверждения. Возможно, изменение поведения между версиями...
Ответ 2
Определенно # 1. Это "выглядит" страшно, но понимайте, что оптимизатор будет поступать правильно и выражает намерение. Кроме того, это небольшой бонус опечатки, если вы случайно думаете EXISTS, но введите IN. # 2 является приемлемым, но не выразительным. Третий вариант воняет в моем не очень скромном мнении. Это слишком близко к тому, чтобы сказать "если" нет ценности "для комфорта.
В целом важно не бояться писать код, который выглядит неэффективно, если он обеспечивает другие преимущества и фактически не влияет на производительность.
То есть оптимизатор почти всегда будет выполнять сложное мастерство соединения/выбора/группировки, чтобы сохранить простой EXISTS/подзапрос таким же образом.
После предоставления kudos для умного переписывания этого неприятного ИЛИ из соединения вы в конечном итоге поймете, что оптимизатор все еще использовал тот же дерьмовый план выполнения для решения гораздо проще понять запрос со встроенным OR в любом случае.
Мораль этой истории - это знать оптимизатор платформ. Попробуйте разные вещи и посмотрите, что на самом деле делается, потому что неумолимые предположения о коленных суставах относительно "декоративной" оптимизации запросов почти всегда неправильны и не имеют отношения к моему опыту.
Ответ 3
Я понимаю, что это старый пост, но я подумал, что важно добавить ясность в том, почему можно выбрать один формат над другим.
Во-первых, как указывали другие, механизм базы данных предположил, чтобы игнорировать предложение Select. Каждая версия SQL Server имеет/делает, Oracle делает, MySQL делает и так далее. Во многих, много лунах разработки баз данных, я только когда-либо сталкивался с одной СУБД, которая неправильно игнорировала предложение Select: Microsoft Access. В частности, более старые версии MS Access (я не могу говорить с текущими версиями).
До моего открытия этой "функции" я использовал Exists( Select *...
. Тем не менее, я обнаружил, что MS Access будет передавать через каждый столбец в подзапросе, а затем отбрасывать их (Select 1/0
тоже не работает). Это заставило меня переключиться на Select 1
. Если даже одна СУБД была глупой, другой мог бы существовать.
Написание Exists( Select 1...
настолько же отчетливо проявляется в передаче намерения (откровенно глупо требовать "слишком близко к высказыванию", если "нет ценности" существует "для удобства" ) и делает шансы СУБД делать что-то глупое с предложением Select почти невозможно. Select Null
будет служить той же цели, но это просто больше символов для записи.
Я переключился на Exists( Select 1
, чтобы убедиться, что СУБД не может быть глупой. Тем не менее, это было много лет назад, и сегодня я ожидаю, что большинство разработчиков ожидали увидеть Exists( Select *
, который будет работать точно так же.
Тем не менее, я могу дать одну вескую причину избежать Exists(Select *
, даже если ваша СУБД правильно оценивает ее. Намного легче найти и запустить все использования Select *
, если вам не нужно пропустить каждый экземпляр его использования в предложении Exists.
Ответ 4
В SQL Server, по крайней мере,
Наименьший объем данных, которые можно считывать с диска, представляет собой единую "страницу" на диске. Как только процессор считывает одну запись, которая удовлетворяет предикатам подзапроса, она может остановиться. Подзапрос не выполняется, как если бы он стоял на нем, а затем включался во внешний запрос, он выполнялся как часть полного плана запроса для всего. Поэтому при использовании в качестве подзапроса действительно неважно, что в предложении Select, во всяком случае ничего не возвращается во внешний запрос, кроме логического, чтобы указать, была ли найдена одна запись или нет...
Все три используют один и тот же план выполнения
Я всегда использую [Select * From...], поскольку я думаю, что он читает лучше, не подразумевая, что я хочу что-то, в частности, возвращено из подзапроса.
EDIT: Из комментария dave costa... Oracle также использует один и тот же план выполнения для всех трех параметров
Ответ 5
EXISTS
возвращает логические не фактические данные, что лучше всего использовать # 3.
Ответ 6
Это один из тех вопросов, который граничит с началом какой-то священной войны.
Там довольно хорошее обсуждение об этом здесь.
Я думаю, что ответ, вероятно, будет использовать третий вариант, но увеличение скорости настолько бесконечно мало, что это действительно не стоит беспокоиться. Это простой запрос, который SQL Server может оптимизировать внутренне, так что вы можете обнаружить, что все параметры эквивалентны.
Ответ 7
План выполнения.
Учите его, используйте его, любите его
Невозможно догадаться, действительно.
Ответ 8
В дополнение к тому, что говорили другие, практика использования SELECT 1
возникла на старом Microsoft SQL Server (предыдущий 2005) - его оптимизатор запросов не был достаточно умен, чтобы избежать физической загрузки полей из таблицы для SELECT *
. Насколько мне известно, ни одна другая СУБД не имеет этого недостатка.
EXISTS проверяет наличие строк, а не то, что в них, так что, помимо некоторых причудливых оптимизаторов, подобных выше, на самом деле не имеет значения, что в списке SELECT.
SELECT *
кажется наиболее обычным, но другие также приемлемы.
Ответ 9
# 3 Должен быть лучшим, так как вам все равно не нужны возвращенные данные. Приведение полей добавит дополнительные служебные данные