Выполнение вариантов использования SQL «EXISTS» - PullRequest
23 голосов
/ 08 января 2009

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

Есть ли лучшая практика, когда все утверждения ведут себя одинаково?

Ответы [ 9 ]

32 голосов
/ 07 ноября 2010

Правда об условии 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 и т. Д.

Этот вопрос не был помечен с точки зрения базы данных, и это должно быть потому, что поставщики работают по-разному - так что тестируйте и проверяйте планы объяснения / выполнения для подтверждения. Возможно, что поведение меняется между версиями ...

15 голосов
/ 08 января 2009

Определенно № 1. Это «выглядит» страшно, но понимаешь, что оптимизатор сделает правильные вещи и выразит намерение. Также есть небольшой бонус за опечатку, если кто-то случайно подумает о СУЩЕСТВОВАНИИ, но набрал IN № 2 приемлемо, но не выразительно. Третий вариант воняет по моему не столь скромному мнению. Слишком близко к слову «если« нет значения »существует» для комфорта.

В общем, важно не бояться писать код, который выглядит просто неэффективно, если он дает другие преимущества и фактически не влияет на производительность.

То есть оптимизатор почти всегда будет выполнять сложное волшебство соединения / выбора / группировки, чтобы сохранить простой подзапрос EXISTS /.

После того, как вы отдадите себе похвалу за умное переписывание этого неприятного ИЛИ из объединения, вы в конечном итоге поймете, что оптимизатор по-прежнему использует тот же самый дрянной план выполнения для решения гораздо более простого для понимания запроса со встроенным ИЛИ в любом случае.

Мораль этой истории в том, что вы знаете оптимизатор ваших платформ. Попробуйте разные вещи и посмотрите, что на самом деле делается, потому что безудержные предположения по поводу «декоративной» оптимизации запросов почти всегда неверны и не имеют отношения к моему опыту.

6 голосов
/ 14 июня 2012

Я понимаю, что это старый пост, но я подумал, что важно добавить ясности о , почему можно выбрать один формат вместо другого.

Во-первых, как уже отмечали другие, ядро ​​базы данных предполагает , чтобы игнорировать предложение 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.

3 голосов
/ 08 января 2009

В SQL Server, по крайней мере,

Наименьшее количество данных, которые можно прочитать с диска, представляет собой одну «страницу» дискового пространства. Как только процессор читает одну запись, которая удовлетворяет предикатам подзапроса, он может остановиться. Подзапрос не выполняется так, как если бы он стоял сам по себе, а затем включается во внешний запрос, он выполняется как часть полного плана запроса для всего этого. Таким образом, при использовании в качестве подзапроса на самом деле не имеет значения, что находится в предложении Select, в любом случае ничего не возвращается «внешнему запросу», кроме логического значения, указывающего, была ли найдена одна запись или нет ...

Все три используют один и тот же план выполнения

Я всегда использую [Select * From ...], так как думаю, что он читается лучше, не подразумевая, что я хочу, чтобы что-то конкретное было возвращено из подзапроса.

РЕДАКТИРОВАТЬ: Из комментария Дэйва Коста ... Oracle также использует один и тот же план выполнения для всех трех вариантов

1 голос
/ 08 января 2009

Это один из тех вопросов, которые граничат с началом какой-то священной войны.

Здесь довольно хорошая дискуссия здесь .

Я думаю, что ответ, вероятно, заключается в использовании третьего варианта, но увеличение скорости настолько бесконечно мало, что действительно не стоит беспокоиться. Это легко тот тип запросов, который SQL Server может оптимизировать внутри системы в любом случае, поэтому вы можете обнаружить, что все параметры эквивалентны.

1 голос
/ 08 января 2009

EXISTS возвращает логическое значение , а не фактические данные, которые, как говорится, рекомендуется использовать # 3.

0 голосов
/ 16 июня 2012

В дополнение к тому, что говорили другие, практика использования SELECT 1 возникла на старом Microsoft SQL Server (до 2005 г.) - его оптимизатор запросов не был достаточно умен, чтобы избежать физического извлечения полей из таблицы для SELECT *. Насколько мне известно, ни одна другая СУБД не имеет такого недостатка.

EXISTS проверяет наличие строк, а не то, что в них, поэтому, кроме некоторой причуды оптимизатора, аналогичной приведенной выше, на самом деле не имеет значения, что находится в списке SELECT.

* * * * * * * * * * * * * * * * * * * '' 100, кажется, наиболее обычно, но другие также приемлемы. * 100 '*

0 голосов
/ 08 января 2009

План выполнения . Изучи это, используй это, люби это

Нет никакого способа угадать, на самом деле.

0 голосов
/ 08 января 2009

# 3 Должен быть лучшим, так как вам все равно не нужны возвращаемые данные. При вводе полей будут добавлены только дополнительные издержки

...