Есть ли разница в производительности следующих трех операторов 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 все поля tableB, одно поле или только константу?
Есть ли лучшая практика, когда все операторы ведут себя одинаково?


# 3 Должен быть лучшим, так как вам все равно не нужны возвращаемые данные. Добавление полей только добавит дополнительных накладных расходов
Я надеюсь, что любой современный оптимизатор запросов к базе данных распознает, что данные подзапроса не используются, и обработает все варианты одинаково.
EXISTS возвращает не фактические данные логический, поэтому рекомендуется использовать №3.
Можете ли вы объяснить, почему вы считаете это лучшей практикой?
@Dave - Я считаю, что исходная мысль школы «если существует (выберите 1 ..)» заключалась в том, что, возвращая константу, SQL получит больше свободы в выборе плана запроса (например, из более узкого индекса NC), тогда как SELECT *, похоже, поощряет поиск / сканирование кластерного индекса. Однако, как указали OMG Ponies, поскольку SQL все равно не оценивает предложение, это становится вопросом читабельности / единообразия, а не производительности. К сожалению, учитывая, что SELECT * обычно является анти-шаблоном в выборках, подобное исключение затрудняет соблюдение стандартов кодирования.
Это один из тех вопросов, который граничит с развязкой священной войны.
Об этом есть довольно хорошее обсуждение здесь.
Я думаю, что ответ, вероятно, состоит в том, чтобы использовать третий вариант, но увеличение скорости настолько бесконечно мало, что действительно не стоит беспокоиться. Это тот тип запроса, который SQL Server в любом случае может оптимизировать изнутри, поэтому вы можете обнаружить, что все параметры эквивалентны.
По крайней мере, в SQL Server
Наименьший объем данных, который может быть прочитан с диска, - это одна «страница» дискового пространства. Как только процессор прочитает одну запись, удовлетворяющую предикатам подзапроса, он может остановиться. Подзапрос не выполняется, как если бы он стоял сам по себе, а затем включается во внешний запрос, он выполняется как часть полного плана запроса для всего этого. Поэтому при использовании в качестве подзапроса на самом деле не имеет значения, что находится в предложении Select, во внешний запрос все равно ничего не возвращается, кроме логического значения, указывающего, была ли найдена отдельная запись или нет ...
Все трое используют один и тот же план выполнения.
Я всегда использую [Select * From ...], поскольку считаю, что он лучше читается, не подразумевая, что я хочу, чтобы что-то, в частности, было возвращено из подзапроса.
Обновлено: Из комментария Дэйва Коста ... Oracle также использует один и тот же план выполнения для всех трех вариантов
В Oracle все три варианта (плюс SELECT NULL) также используют один и тот же план выполнения. Примечательно, что даже в плане SELECT * не было доступа к tableB, только к индексу в столбце соединения, поэтому оптимизатор явно понимает, что ему не нужны фактические значения для SELECT *.
* = ничего? Я не понимаю. Фактически * = все, NULL = ничего. Если вы хотите «не [подразумевать], что я хочу вернуть что-то конкретное», будет ли SELECT NULL намного яснее.
@Mark, внутри подзапроса Where Exists, содержимое предложения select не имеет значения ... Так что не только * = ничего, это также = каждая вещь, что угодно, все и / или что-то ... т.е. это не имеет значения ... Причина, по которой вы должны указать там "что-то", заключается в том, что этого требует предложение Select.
Определенно №1. Это «выглядит» пугающе, но поймите, что оптимизатор будет делать правильные вещи и выражает намерения. Также есть небольшой бонус за опечатку, если кто-то случайно подумает, что СУЩЕСТВУЕТ, но наберет IN. # 2 приемлемо, но не выразительно. Третий вариант воняет, на мой не столь скромный взгляд. Для удобства это слишком близко к словам «если не существует никакой ценности».
В общем, важно не бояться писать код, который выглядит неэффективным, если он дает другие преимущества и фактически не влияет на производительность.
То есть оптимизатор почти всегда будет выполнять ваше сложное волшебство соединения / выбора / группировки, чтобы таким же образом сохранить простой EXISTS / подзапрос.
После того, как вы предоставили себе престиж для умного переписывания этого неприятного OR из соединения, вы в конечном итоге поймете, что оптимизатор все равно использовал тот же дерьмовый план выполнения для решения гораздо более простого для понимания запроса со встроенным OR.
Мораль этой истории - знай оптимизатор своей платформы. Попробуйте разные вещи и посмотрите, что на самом деле делается, потому что безудержные предположения коленных рефлексов относительно «декоративной» оптимизации запросов почти всегда неверны и не имеют отношения к моему опыту.
+1, потому что это единственный ответ, который дал повод предпочесть один вариант другому вместо того, чтобы просто сказать: «X - лучшая практика».
Плакат не спрашивал причин, а просто передовой опыт!
Это все равно, что сказать: «Меня спрашивали не для обоснования моего ответа» на экзамене по философии ... Вы даете ответ и хотите, чтобы люди обратили внимание, вы оправдываете свой ответ.
Предложение SELECT не оценивается в EXISTS - подробности см. В моем ответе.
@ Демс: эта аналогия не имеет смысла.
@Mitch - ответ на комментарий, сделанный более 20 месяцев назад? И хватило ли смысла на 3 голоса за?
@Mitch - Верно, но с учетом низкой склонности комментариев к привлечению голосов и в сочетании с датой, с которой это стало возможным; 3 голоса - статистически значимый показатель внешнего согласия. Или что-то.
@Dems: в любом случае. Я считаю, что эта аналогия бессмысленна; ввиду того факта, что мой первоначальный ответ был слегка язвительным. Но юмор не очень нравится в комментариях, и разработчики - не самая вероятная группа людей, которая его заметит!
@DaveCosta - Действительно ли "это выглядит страшно" веская причина для предпочтения стиля?
План выполнения. Узнай это, используй, полюби это
На самом деле, нет никакого способа угадать.
Правда о предложении 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 и Beyond - WHERE, GROUP BY, HAVING и т. д.
Этот вопрос не был отмечен для базы данных, и это должно быть связано с тем, что поставщики обрабатывают вещи по-другому - поэтому проверьте и проверьте планы объяснения / выполнения для подтверждения. Возможно, что поведение меняется от версии к версии ...
+1 особенно для примера и утверждения «продавцы по-разному».
Я понимаю, что это старый пост, но я подумал, что важно внести ясность в Зачем: можно выбрать один формат вместо другого.
Во-первых, как указывали другие, механизм базы данных - это предполагаемый, чтобы игнорировать предложение 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.
В дополнение к тому, что говорили другие, практика использования SELECT 1 возникла на старом Microsoft SQL Server (до 2005 года) - его оптимизатор запросов был недостаточно умен, чтобы избежать физического извлечения полей из таблицы для SELECT *. Насколько мне известно, ни одна другая СУБД не имеет этого недостатка.
EXISTS проверяет наличие строк, а не то, что в них содержится, поэтому, кроме некоторой причуды оптимизатора, подобной описанной выше, на самом деле не имеет значения, что находится в списке SELECT.
SELECT * кажется самым обычным, но другие тоже приемлемы.
Вы выбрали неправильный ответ.