Всякий раз, когда мне нужно проверить наличие какой-либо строки в таблице, я всегда пишу условие вроде:
SELECT a, b, c
FROM a_table
WHERE EXISTS
(SELECT * -- This is what I normally write
FROM another_table
WHERE another_table.b = a_table.b
)
Некоторые другие люди пишут это так:
SELECT a, b, c
FROM a_table
WHERE EXISTS
(SELECT 1 --- This nice '1' is what I have seen other people use
FROM another_table
WHERE another_table.b = a_table.b
)
Когда условие NOT EXISTS
вместо EXISTS
: В некоторых случаях я мог бы написать его с дополнительным LEFT JOIN
и дополнительным условием (иногда называемым антисоединением ):
SELECT a, b, c
FROM a_table
LEFT JOIN another_table ON another_table.b = a_table.b
WHERE another_table.primary_key IS NULL
Я стараюсь избегать этого, потому что я думаю, что смысл менее ясен, особенно когда то, что у вас primary_key
не так очевидно, или когда ваш первичный ключ или ваше условие соединения состоит из нескольких столбцов (и вы можете легко забыть один из столбцов). Однако иногда вы поддерживаете код, написанный кем-то другим ... и он просто есть.
Есть ли разница (кроме стиля) для использования
SELECT 1
вместоSELECT *
?
Есть ли угловой случай, когда он не ведет себя так же?Хотя то, что я написал, является стандартным SQL (AFAIK): есть ли такая разница для разных баз данных / более старых версий?
Есть ли какое-то преимущество в простоте написания противодействия?
Современные планировщики / оптимизаторы рассматривают это иначе, чемNOT EXISTS
пункт?
источник
EXISTS (SELECT FROM ...)
.Ответы:
Нет, нет никакой разницы в эффективности между
(NOT) EXISTS (SELECT 1 ...)
и(NOT) EXISTS (SELECT * ...)
во всех основных СУБД. Я также часто видел(NOT) EXISTS (SELECT NULL ...)
, как его используют.В некоторых вы можете даже написать,
(NOT) EXISTS (SELECT 1/0 ...)
и результат тот же - без какой-либо (деление на ноль) ошибки, которая доказывает, что выражение там даже не вычисляется.О
LEFT JOIN / IS NULL
методе против присоединения, исправление: это эквивалентноNOT EXISTS (SELECT ...)
.В этом случае
NOT EXISTS
противLEFT JOIN / IS NULL
Вы можете получить разные планы выполнения. Например, в MySQL и в основном в более старых версиях (до 5.7) планы были бы довольно похожими, но не идентичными. Оптимизаторы других СУБД (SQL Server, Oracle, Postgres, DB2) - насколько я знаю - более или менее способны переписать эти два метода и учитывать одинаковые планы для обоих. Тем не менее, нет такой гарантии, и при выполнении оптимизации полезно проверять планы из разных эквивалентных перезаписей, поскольку могут быть случаи, когда каждый оптимизатор не переписывает (например, сложные запросы со многими объединениями и / или производными таблицами / подзапросы внутри подзапроса, где условия из нескольких таблиц, составных столбцов, используемых в условиях соединения) или выбор и планы оптимизатора по-разному зависят от доступных индексов, настроек и т. д.Также обратите внимание, что
USING
не может использоваться во всех СУБД (например, SQL Server). Более распространенныеJOIN ... ON
работы везде.И столбцы должны иметь префикс с именем таблицы / псевдонимом в,
SELECT
чтобы избежать ошибок / неясностей, когда у нас есть объединения.Я также обычно предпочитаю ставить объединенный столбец в
IS NULL
чек (хотя PK или любой необнуляемый столбец будет в порядке, это может быть полезно для эффективности, когда планLEFT JOIN
использует некластеризованный индекс):Существует также третий метод для антисоединений, использующий,
NOT IN
но он имеет другую семантику (и результаты!), Если столбец внутренней таблицы обнуляется. Однако его можно использовать, исключив строки сNULL
помощью запроса, эквивалентного предыдущим 2 версиям:Это также обычно дает аналогичные планы в большинстве СУБД.
источник
[NOT] IN (SELECT ...)
, хотя и эквивалентные, работали очень плохо. Избегай это!SELECT *
конечно делает больше работы. Я бы для простоты посоветовал использоватьSELECT 1
Существует одна категория случаев, когда
SELECT 1
иSELECT *
они не являются взаимозаменяемыми - более конкретно, один всегда будет принят в тех случаях, а другой в основном не будет.Я говорю о случаях, когда нужно проверять наличие строк сгруппированного набора. Если в таблице
T
есть столбцыC1
иC2
вы проверяете наличие групп строк, соответствующих определенному условию, вы можете использоватьSELECT 1
это так:но вы не можете использовать
SELECT *
таким же образом.Это просто синтаксический аспект. Если оба варианта приняты синтаксически, вы, скорее всего, не будете иметь различий с точки зрения производительности или возвращаемых результатов, как было объяснено в другом ответе .
Дополнительные примечания после комментариев
Похоже, что не многие продукты баз данных действительно поддерживают это различие. Такие продукты, как SQL Server, Oracle, MySQL и SQLite, с радостью примут
SELECT *
в приведенном выше запросе без каких-либо ошибок, что, вероятно, означает, что они обрабатывают EXISTSSELECT
особым образом.PostgreSQL - это одна СУБД, в которой
SELECT *
может произойти сбой, но в некоторых случаях она может работать. В частности, если вы группируете по PK, всеSELECT *
будет работать нормально, иначе произойдет сбой с сообщением:источник
GROUP BY
, понятие*
бессмысленно (или, по крайней мере, не так ясно).Интересный способ переписать
EXISTS
предложение, которое приводит к более чистому и, возможно, менее вводящему в заблуждение запросу, по крайней мере в SQL Server, будет следующим:Анти-полусоединение версия будет выглядеть так:
Оба, как правило, оптимизированы для того же плана, что и
WHERE EXISTS
илиWHERE NOT EXISTS
, но намерение безошибочно, и у вас нет «странного»1
или*
.Интересно, что проблемы нулевой проверки, связанные с
NOT IN (...)
, проблематичны<> ALL (...)
, в то время как проблемаNOT EXISTS (...)
не страдает от этой проблемы. Рассмотрим следующие две таблицы с обнуляемым столбцом:Мы добавим некоторые данные к обоим, с некоторыми совпадающими строками, а некоторые - нет:
NOT IN (...)
Запрос:Имеет следующий план:
Запрос не возвращает строк, поскольку значения NULL не позволяют подтвердить равенство.
Этот запрос, с
<> ALL (...)
показывает тот же план и не возвращает строк:Вариант с использованием
NOT EXISTS (...)
показывает немного другую форму плана и возвращает строки:План:
Результаты этого запроса:
Это делает использование
<> ALL (...)
столь же склонным к проблемным результатам, как иNOT IN (...)
.источник
*
странным: я читаюEXISTS (SELECT * FROM t WHERE ...)
КАКthere is a _row_ in table _t_ that...
. Во всяком случае, мне нравится иметь альтернативы, и ваш ясно читается. Одно сомнение / предостережение: как он будет себя вести, еслиb
обнуляется? [У меня были плохие переживания и несколько коротких ночей, когда я пытался выяснить ошибку, вызванную ax IN (SELECT something_nullable FROM a_table)
]«Доказательством» того, что они идентичны (в MySQL) является
затем повторите с
SELECT 1
. В обоих случаях «расширенный» вывод показывает, что он был преобразован вSELECT 1
.Точно так же
COUNT(*)
превращается вCOUNT(0)
.Еще одна вещь, которую стоит отметить: улучшения оптимизации были сделаны в последних версиях. Возможно, стоит сравнить с
EXISTS
анти-объединениями. Ваша версия может лучше работать с одним против другого.источник
В некоторых базах данных эта оптимизация еще не работает. Как, например, в PostgreSQL Начиная с версии 9.6, это не удастся.
И это удастся.
Это терпит неудачу, потому что следующее терпит неудачу, но это все еще означает, что есть разница.
Вы можете найти больше информации об этой специфической причуде и нарушении спецификации в моем ответе на вопрос, требует ли спецификация SQL GROUP BY в EXISTS ()
источник
Я всегда использовал
select top 1 'x'
(SQL Server)Теоретически,
select top 1 'x'
было бы более эффективно, чтоselect *
, поскольку первое будет завершено после выбора константы о существовании квалифицирующей строки, тогда как второе выберет все.ОДНАКО, хотя очень рано это могло быть актуально, оптимизация сделала разницу неактуальной, вероятно, во всех основных RDBS.
источник
top n
безorder by
хорошей идеи.select top 1 'x'
не должно быть более эффективным, чемselect *
вExist
выражении. Практически это может быть более эффективно, если оптимизатор работает неоптимально, но теоретически оба выражения эквивалентны.IF EXISTS(SELECT TOP(1) 1 FROM
это лучшая привычка в долгосрочной перспективе и для разных платформ просто потому, что вам даже не нужно беспокоиться о том, насколько хороша или плоха ваша текущая платформа / версия; и SQL движетсяTOP n
к параметризуемомуTOP(n)
. Это должен быть навык однократного обучения.источник
TOP
это даже не допустимый SQL.TOP (n)
в «SQL» - стандартного языка запросов. Существует один на T-SQL, который является диалектом, который использует Microsoft SQL Server.