Exists select oracle: EXISTS Oracle/PLSQL — Oracle PL/SQL •MySQL •MariaDB •SQL Server •SQLite
EXISTS SQL и проверка существования набора значений
Предикат языка SQL EXISTS выполняет логическую задачу. В запросах SQL этот предикат используется
в выражениях вида
EXISTS (SELECT * FROM ИМЯ_ТАБЛИЦЫ…).
Это выражение возвращает истину, когда по запросу найдена одна или более строк, соответствующих
условию, и ложь, когда не найдено ни одной строки.
Обычно предикат EXISTS применяется в случаях, когда необходимо найти значения, соответствующие
основному условию, заданному в секции WHERE, и дополнительному условию, заключённому в подзапрос,
являющийся аргументом предиката.
Для NOT EXISTS всё наоборот. Выражение
NOT EXISTS (SELECT * FROM ИМЯ_ТАБЛИЦЫ…)
возвращает истину, когда по запросу не найдено ни одной строки, и ложь, когда найдена
хотя бы одна строка.
Если вы хотите выполнить запросы к базе данных из этого урока на MS SQL Server, но эта СУБД
не установлена на вашем компьютере, то ее можно установить, пользуясь инструкцией по этой ссылке.
В примерах работаем с базой данных библиотеки и ее таблицами «Книга в пользовании» (BOOKINUSE) и
«Пользователь» (USER). Пока нам потребуется лишь таблица «Книга в пользовании» (BOOKINUSE).
Скрипт для создания базы данных библиотеки, её таблиц и заполения таблиц данными —
в файле по этой ссылке.
Author | Title | Pubyear | Inv_No | Customer_ID |
Толстой | Война и мир | 2005 | 28 | 65 |
Чехов | Вишневый сад | 2000 | 17 | 31 |
Чехов | Избранные рассказы | 2011 | 19 | 120 |
Чехов | Вишневый сад | 1991 | 5 | 65 |
Ильф и Петров | Двенадцать стульев | 1985 | 3 | 31 |
Маяковский | Поэмы | 1983 | 2 | 120 |
Пастернак | Доктор Живаго | 2006 | 69 | 120 |
Толстой | Воскресенье | 2006 | 77 | 47 |
Толстой | Анна Каренина | 1989 | 7 | 205 |
Пушкин | Капитанская дочка | 2004 | 25 | 47 |
Гоголь | Пьесы | 2007 | 81 | 47 |
Чехов | Избранные рассказы | 1987 | 4 | 205 |
Пушкин | Сочинения, т. 1 | 1984 | 6 | 47 |
Пастернак | Избранное | 2000 | 137 | 18 |
Пушкин | Сочинения, т.2 | 1984 | 8 | 205 |
NULL | Наука и жизнь 9 2018 | 2019 | 127 | 18 |
Чехов | Ранние рассказы | 2001 | 171 | 31 |
Пример 1. Определить ID пользователей, которым выданы книги
Толстого, которым также выданы книги Чехова. Во внешнем запросе отбираются данные о пользователях,
которым выданы книги Толстого, а предикат EXISTS задаёт дополнительное условие, которое проверяется в
во внутреннем запросе — пользователи, которым выданы книги Чехова. Дополнительным условием во внутреннем
запросе является совпадение идентификаторов пользователей из внешнего и внутреннего запросов:
Customer_ID=tols_user.Customer_id. Запрос будет следующим:
SELECT Customer_ID FROM Bookinuse
AS tols_user
WHERE Author=’Толстой’
AND EXISTS (SELECT
Customer_ID FROM Bookinuse
WHERE Author=’Чехов’
AND Customer_ID=tols_user. Customer_id)
Этот запрос вернёт следующий результат:
Далее — пример использования NOT EXISTS в запросе, решающем похожую задачу.
Пример 2. Определить ID пользователей, которым выданы книги
Чехова, и которым при этом не выданы книги Ильфа и Петрова. Конструкция запроса аналогична конструкции из
предыдущего примера с той разницей, что дополнительное условие задаётся предикатом NOT EXISTS. Запрос будет следующим:
SELECT Customer_ID FROM Bookinuse
AS cheh_user
WHERE Author=’Чехов’
AND NOT EXISTS (SELECT
Customer_ID FROM Bookinuse
WHERE Author=’Ильф и Петров’
AND Customer_ID=cheh_user.Customer_id)
Этот запрос вернёт следующий результат:
Написать запрос SQL с предикатом EXISTS самостоятельно, а затем посмотреть решение
При первом взгляде на запросы с предикатом EXISTS может возникнуть впечатление, что он идентичен
предикату IN. Это не так. Хотя они
очень похожи. Предикат
IN ведет поиск значений из диапазона, заданного в его аргументе, и если такие значения есть, то
выбираются все строки, соответствующие этому диапазону. Результат же действия предиката EXISTS
представляет собой ответ «да» или «нет» на вопрос о том, есть ли вообще какие-либо значения, соответствующие
указанным в аргументе. Кроме того, перед предикатом IN указывается имя столбца, по которому следует
искать строки, соответствующие значениям в диапазоне. Разберём пример, показывающий отличие предиката
EXISTS от предиката IN, и задачу, решаемую с помощью предиката IN.
Пример 4. Определить ID пользователей, которым выданы книги
авторов, книги которых выданы пользователю с ID 31. Запрос будет следующим:
SELECT Customer_ID
FROM Bookinuse WHERE Author IN (SELECT
Author FROM Bookinuse
WHERE Customer_ID=31)
Результатом выполнения запроса будет следующая таблица:
Внутренний запрос (после IN) выбирает авторов: Чехов; Ильф и Петров. Внешний запрос
выбирает всех пользователей, которым выданы книги этих авторов. Видим, что, в отличие от предиката EXISTS,
предикат IN предваряется именем столбца, в данном случае — Author.
Если дополнительно к предикату EXISTS в запросе применить хотя бы одно дополнительное условие,
например, заданное с помощью агрегатных функций, то такие запросы могут служить уже для простого
анализа данных. Продемонстрируем это на следующем примере.
Пример 5. Определить ID пользователей, которым выдана
хотя бы одна книга Пастернака, и которым при этом выдано более 2 книг. Пишем следующий запрос, в котором
первое условие задаётся предикатом EXISTS со вложенным запросом, а второе условие с оператором HAVING
всегда должно следовать после вложенного запроса:
SELECT Customer_ID FROM Bookinuse
AS pas_user
WHERE EXISTS (SELECT
Customer_ID FROM Bookinuse
WHERE Author=’Пастернак’
AND Customer_ID=pas_user. Customer_ID)
GROUP BY Customer_ID
HAVING COUNT(Title) > 2
Результат выполнения запроса:
Как видно из таблицы BOOKINUSE, книга Пастернака выдана также пользователю с
ID 18, но ему выдана всего одна книга и он не попадает в выборку. Если применить к подобному запросу ещё раз
функцию COUNT, но уже для подсчёта выбранных строк (потренируйтесь в этом самостоятельно), то
можно получить сведения о том, сколько пользователей, читающих книги Пастернака, при этом читают
также книги других авторов. Это уже из сферы анализа данных.
Запросы с предикатом EXISTS могут извлекать данные из более чем одной таблицы. Многие задачи можно с
тем же результатом решить с помощью оператора JOIN,
но в ряде случаев использование EXISTS позволяет составить менее громоздкий запрос. Использовать EXISTS
предпочительнее в тех случаях, когда в результирующую таблицу попадут столбцы лишь из одной таблицы.
В следующем примере из той же базы данных помимо таблицы BOOKINUSE потребуется также таблица
«Пользователь» (CUSTOMER).
Customer_ID | Surname |
18 | Зотов |
31 | Перов |
47 | Васин |
65 | Тихонов |
120 | Краснов |
205 | Климов |
Пример 6. Определить авторов, книги которых выданы пользователю
по фамилии Краснов. Пишем следующий запрос, в котором предикатом EXISTS задано единственное условие:
SELECT DISTINCT Author
FROM Bookinuse bk
WHERE EXISTS (SELECT *
FROM Customer cs
WHERE cs.Customer_ID=bk.Customer_ID
AND Surname=’Краснов’)
Результатом выполнения запроса будет следующая таблица:
Author |
Чехов |
Маяковский |
Пастернак |
Как и в случае использования оператора JOIN, в случаях более одной таблицы следует
использовать псевдонимы таблиц для проверки соответствия значений ключей, соединяющих таблицы.
В нашем примере псевдонимы таблиц — bk и us, а ключ, соединяющий таблицы — User_ID.
Примеры запросов к базе данных «Библиотека» есть также в уроках по операторам GROUP BY,
IN и функциям
CONCAT, COALESCE.
Сейчас мы увидим более предметно, почему использовать EXISTS
предпочительнее в тех случаях, когда в результирующую таблицу попадут столбцы лишь из одной таблицы.
Работаем с базой данных «Недвижимость». Скрипт для создания этой базы данных, её таблиц и заполения таблиц данными —
в файле по этой ссылке.
Таблица Deal содержит данные о сделках. Для наших
заданий в этой таблице будет важен столбец Type с данными о типе сделки — продажа или аренда. Таблица
Object содержит данные об объектах. В этой таблице нам понадобятся значения столбцов Rooms (число комнат) и LogBalc, содержащего
данные о наличии лоджии или балкона в булевом формате: 1 (да) или 0 (нет). Таблицы Client, Manager и Owner
содержат данные соответственно о клиентах, менеджерах фирмы и собственниках объектов недвижимости. В этих
таблицах FName и LName соответственно имя и фамилия.
Пример 7. Определить клиентов, купивших или взявших в аренду
объекты, у которых нет лоджии или балкона. Пишем следующий запрос, в котором предикатом EXISTS
задано обращение к результату соединения двух таблиц:
SELECT cl.* FROM Client cl
WHERE EXISTS (SELECT 1
FROM Deal de JOIN Object ob
ON ob.Obj_ID=de.Object_ID
WHERE de.Client_ID=cl.Client_ID
AND ob.LogBalc=0)
Так как из таблицы Client столбцы выбираются при помощи оператора «звёздочка», то
будут выведены все столбцы этой таблицы, в которой будет столько строк, сколько насчитывается клиентов,
соответствующих условию, заданному предикатом EXISTS. Из таблиц, к соединению которых обращается вложенный
запрос, нам не требуется выводить ни одного столбца. Поэтому для экономии машинного времени извлекается
лишь один столбец. Для этого после слова SELECT прописана единица. Этот же приём применён и в запросах
в следующих примерах.
Написать запрос SQL с предикатом EXISTS самостоятельно, а затем посмотреть решение
Пример 3. Определить менеджеров, которые провели сделки с объектами
с числом комнат больше 2.
Правильное решение.
Продолжаем писать вместе запросы SQL с предикатом EXISTS
Пример 9. Определить собственников объектов, которые были
взяты в аренду. Пишем следующий запрос, в котором предикатом EXISTS также
задано обращение к результату соединения двух таблиц:
SELECT ow.* FROM Owner ow
WHERE EXISTS (SELECT 1
FROM Object ob JOIN Deal de
ON de.Object_ID=ob.Obj_ID
WHERE ow.Owner_ID=ob.Owner_ID
AND de. Type=’rent’)
Как и в предыдущем примере, из таблицы, к которой обращён внешний запрос, будут
выведены все поля.
Пример 10. Определить число собственников, с объектами которых
провёл менеджер Савельев. Пишем запрос, в котором внешний запрос обращается к соединению трёх таблиц, а
предикатом EXISTS задано обращение лишь к одной таблице:
SELECT COUNT(*) FROM Object ob
JOIN Deal de ON
de.Object_ID=ob.Obj_ID
JOIN Owner ow ON
ob.Owner_ID=ow.Owner_ID
WHERE EXISTS (SELECT 1
FROM Manager ma WHERE
de.Manager_ID=ma.Manager_ID
AND ma.LName=’Савельев’)
Все запросы проверены на существующей базе данных. Успешного использования!
Примеры запросов к базе данных «Недвижимость» есть также в уроках по операторам GROUP BY и IN.
Поделиться с друзьями
Реляционные базы данных и язык SQL
sql — Использование IF EXISTS (SELECT…) в триггере BEFORE INSERT (Oracle)
Во-первых, если вы используете SQL * Plus, когда вы создаете объект и получаете сообщение об ошибках компиляции, команда —- +: = 0 =: + —- покажет вам ошибки.Если вы запустили —- +: = 1 =: + —- , вам скажут, что —- +: = 2 =: + —- недопустимый синтаксис.Вы могли бы сделать что-то вродеОднако, исправив ошибку компиляции, вы получите ошибки времени выполнения.В триггере уровня строки —- +: = 4 =: + —- вы не можете вообще запросить —- +: = 5 =: + —- (вы можете, если все, что вы делаетеявляется —- +: = 6 =: + —-, который гарантированно вставит только одну строку).Если вы сделаете это, вы получите мутирующую ошибку триггера во время выполнения.С точки зрения модели данных, когда вы разрабатываете таблицу, в которой действительные данные для конкретной строки зависят от данных, хранящихся в других строках той же таблицы, вы обычно нарушаете принципы нормализации и, как правило, лучше справляетесь с исправлениембазовая модель данных. Если вы действительно намерены сохранить модель данных, я бы предпочел создать материализованное представление, которое обновляется при фиксации, которое содержит данные только для строк, которые нарушают ваши критерии.Затем вы можете наложить ограничения на это материализованное представление, которое генерирует ошибки во время фиксации, когда ваши критерии нарушаются.Это потребует материализованного просмотра журналов на вашем столе.Если вы действительно хотите сохранить модель данных и хотите применить логику с помощью триггеров, вам понадобится классическое решение с тремя триггерами (или составной триггер с тремя частями, если вы используете 11.2 или более позднюю версию).Вы бы создали пакет с набором значений первичного ключа.Триггер оператора before инициализирует коллекцию.Триггер на уровне строк вставит первичные ключи строк, которые были вставлены и /или обновлены в эту коллекцию.И затем триггер после оператора будет перебирать эту коллекцию и реализовывать любые проверки, которые вы захотите. Это очень много движущихся вещей, поэтому я обычно советую против этого.Кроме того, даже если все эти части работают, ваша логика не защитит вас в многопользовательской среде.Когда в систему одновременно попадают несколько пользователей, вполне возможно, что один пользователь вставит строку, второй пользователь вставит другую строку с перекрывающимся диапазоном, а затем каждый сеанс будет зафиксирован.В этом случае оба набора триггеров позволят внести изменения, но вы все равно останетесь с данными в таблице, которые нарушают ваши требования.Материализованное представление, поскольку оно применяется во время фиксации, а не во время вставки, будет работать правильно в многопользовательской среде.Если вы хотите, чтобы триггеры работали в многопользовательской среде, вам нужно будет еще больше усложнить их, добавив дополнительную логику, обеспечивающую сериализацию, которая блокирует второй сеанс —- +: = 7 =: + —-от запуска до первого сеанса либо зафиксированного, либо откатанного.Это добавляет сложности, снижает масштабируемость и, в зависимости от того, как это реализовано, может стать кошмаром для поддержки.
ответил Justin Cave 28 MarpmFri, 28 Mar 2014 19:51:23 +04002014-03-28T19:51:23+04:0007 2014, 19:51:23
Разница между oracle in и exist
Сравнение Oracle в и существует
«Exists» и «in» находятся в Oracle. Оба они запрашивают, существует ли значение определенной коллекции в другой коллекции, но они по-разному используются для разных данных, в основном из-за большой разницы в эффективности.
select * from table_1 t where t.id in(select id from table_2)
В приведенном выше запросе используется оператор in, in () выполняется только один раз, он находит все поля id в таблице table_2 и кэширует их. После этого он проверяет, равен ли идентификатор таблицы table_1 идентификатору в таблице table_2, и если он равен, это будет table_1 Записи таблицы добавляются к набору результатов до тех пор, пока не будут пройдены все записи таблицы table_1.
Поэтому, когда данные таблицы table_2 большие, их нельзя использовать в (), потому что они будут проходить через все данные таблицы table_2 один раз.
Например: таблица table_1 имеет 10 000 записей, таблица table_2 имеет 1 000 000 записей, тогда можно пройти не более 10 000 * 1 000 000 раз, что очень неэффективно.
Вывод: in () подходит для случая, когда таблица table_2 меньше данных таблицы table_1
select * from table_1 t1 where exists(select 1 from table_2 t2 where t1.id=t2.id)
В приведенном выше запросе используется оператор exists, exists () будет выполнять table_1.length раз, он не кэширует набор результатов exists (), потому что содержимое набора результатов exists () не важно, важно, есть ли записи в наборе результатов, если Верните true, если есть, и false, если нет.
Когда таблица table_2 больше, чем данные таблицы table_1, подходит exists (), потому что у нее нет этой операции обхода, и нужно только выполнить запрос еще раз.
Например: таблица_1 имеет 10 000 записей, а таблица_2 — 1 000 000 записей, тогда существует () будет выполняться 10 000 раз, чтобы определить, равен ли идентификатор в таблице_1 идентификатору в таблице_2.
Например: table_1 имеет 10000 записей, table_2 имеет 100000000 записей, тогда exists () по-прежнему выполняется 10000 раз, поскольку он выполняет только table_1.length раз, видно, что чем больше данных в таблице table_2, тем больше подходит для exists () для воспроизведения эффекта.
Вывод: exists () подходит для ситуации, когда таблица table_2 больше, чем данные таблицы table_1
Когда данные таблицы table_1 имеют такой же размер, как данные таблицы table_2, in и exists схожи по эффективности, и вы можете выбрать один для использования.
sql — Использование IF EXISTS (SELECT …) в триггере BEFORE INSERT (Oracle)
Во-первых, если вы используете SQL * Plus, когда вы создаете объект и получаете сообщение об ошибках компиляции, команда show errors
покажет вам ошибки.
Если вы запустили , покажите ошибки
, вам скажут, что IF EXISTS
не является допустимым синтаксисом. Вы можете сделать что-то вроде
ВЫБРАТЬ СЧЕТЧИК (*)
INTO l_cnt
FROM << остальная часть запроса >>
ЕСЛИ (l_cnt> 0)
ТОГДА
RAISE_APPLICATION_ERROR. ..
КОНЕЦ ЕСЛИ;
Однако, как только вы исправите ошибку компиляции, вы получите ошибки времени выполнения. В триггере на уровне строки на Наблюдение
вы не можете обычно запрашивать Наблюдение
(вы можете, если все, что вы делаете, это INSERT VALUES
, который гарантированно вставляет только одну строку). Если вы это сделаете, вы получите ошибку мутирующего триггера во время выполнения.
С точки зрения модели данных, когда вы обнаруживаете, что разрабатываете таблицу, в которой допустимые данные для конкретной строки зависят от данных, хранящихся в других строках той же таблицы, вы обычно нарушаете принципы нормализации, и вам, как правило, лучше исправлять базовая модель данных.
Если вы действительно настроены сохранить модель данных, я бы предпочел создать материализованное представление, которое обновляется при фиксации, которое содержит данные только для строк, нарушающих ваши критерии. Затем вы можете наложить ограничения на это материализованное представление, которое выдает ошибки во время фиксации, когда ваши критерии нарушаются. Для этого потребуются материализованные журналы просмотра на вашем столе.
Если вы действительно хотите сохранить модель данных и хотите обеспечить соблюдение логики с помощью триггеров, вам понадобится классическое решение с тремя триггерами (или составной триггер с тремя частями, если вы используете 11.2 или новее). Вы бы создали пакет с набором значений первичного ключа. Триггер перед оператором инициализирует коллекцию. Триггер на уровне строки вставляет первичные ключи строк, которые были вставлены и / или обновлены в эту коллекцию. И затем триггер после оператора будет перебирать эту коллекцию и выполнять любые проверки, которые вы хотите. Тем не менее, это много трогательных моментов, поэтому я обычно не рекомендую этого делать.
Кроме того, даже если у вас все эти части заработают, ваша логика не защитит вас в многопользовательской среде.Когда у вас есть несколько пользователей, обращающихся к системе одновременно, вполне возможно, что один пользователь вставит строку, второй пользователь вставит другую строку с перекрывающимся диапазоном, а затем каждый сеанс будет фиксироваться. В этом случае оба набора триггеров разрешат изменение, но вы все равно останетесь с данными в таблице, которые нарушают ваши требования. Материализованное представление, поскольку оно применяется во время фиксации, а не во время вставки, будет правильно работать в многопользовательской среде.Если вы хотите, чтобы триггеры работали в многопользовательской среде, вам придется еще больше усложнить их, добавив дополнительную логику, которая обеспечивает сериализацию, которая блокирует запуск вставки
второго сеанса до тех пор, пока первый сеанс не будет зафиксирован или откат. Это добавляет сложности, снижает масштабируемость и, в зависимости от того, как это реализовано, может стать кошмаром для поддержки.
Рассмотрите возможность использования [NOT] EXISTS вместо [NOT] IN с подзапросом (PE019)
- Подсказка SQL
- Анализ кода SQL
Фил Фактор объясняет, почему вы должны предпочесть использование [NOT] EXISTS перед [NOT] IN при сравнении наборов данных с помощью подзапроса. Хотя значительного преимущества в производительности больше нет, использование NOT EXISTS позволит избежать неожиданных результатов, когда исходные данные подзапроса содержат значения NULL.
Гостевой пост
Это гостевой пост от Фила Фактора. Фил Фактор (настоящее имя не разглашается, чтобы защитить виновных), он же Database Mole, имеет 30-летний опыт работы с приложениями, интенсивно использующими базы данных.
Несмотря на то, что однажды на выставке в начале 1980-х на него кричал разъяренный Билл Гейтс, он оставался абсолютно анонимным на протяжении всей своей карьеры.
Он является постоянным участником Simple Talk и SQLServerCentral .
Раньше логический оператор EXISTS был быстрее, чем IN, при сравнении наборов данных с помощью подзапроса. Например, в случаях, когда запрос должен был выполнить определенную задачу, но только если подзапрос вернул какие-либо строки, тогда при оценке WHERE [NOT] EXISTS (подзапрос) ядро базы данных может прекратить поиск, как только найдет только одну row, тогда как WHERE [NOT] IN (подзапрос) будет всегда собирать все результаты подзапроса перед дальнейшей обработкой.
Однако оптимизатор запросов теперь обрабатывает EXISTS и IN одинаково, когда это возможно, поэтому вы вряд ли заметите какие-либо существенные различия в производительности. Тем не менее, вы должны быть осторожны при использовании оператора NOT IN, если исходные данные подзапроса содержат значения NULL. Если это так, вам следует рассмотреть возможность использования оператора NOT EXISTS вместо NOT IN или преобразовать оператор в левое внешнее соединение.
Рекомендация предпочитать использование [NOT] EXISTS перед [NOT] IN »включена в качестве правила анализа кода в запрос SQL (PE019).
Что работает лучше: EXISTS или IN….?
Существует множество способов устранения различий между двумя наборами данных, но два из наиболее распространенных — это использование логического оператора EXISTS
или IN
. Представьте, что у нас есть две простые таблицы, одна со всеми общеупотребительными словами английского языка ( CommonWords
), а другая со списком всех слов из «Dracula» Брэма Стокера ( WordsInDracula
). Загрузка TestExistsAndIn включает сценарий для создания этих двух таблиц и заполнения каждой из связанных с ней текстовых файлов.Как правило, полезно иметь такие таблицы на вашем сервере песочницы для запуска тестов во время разработки, хотя книга, которую вы используете, — это ваш выбор!
Сколько слов в Дракуле не являются общеупотребительными? Предполагая, что в столбце CommonWords.Word
нет значений NULL
(подробнее об этом позже), следующие запросы вернут тот же результат (1555 слов) и будут иметь тот же план выполнения, который использует соединение слиянием (Правое анти-полусоединение) между двумя столами.
—использование NOT IN SELECT Count (*) FROM dbo.WordsInDracula WHERE word NOT IN (SELECT CommonWords.word FROM dbo.CommonWords); —Использование NOT EXISTS SELECT Count (*) FROM dbo.WordsInDracula WHERE NOT EXISTS (SELECT * FROM dbo. CommonWords WHERE CommonWords.word = WordsInDracula.слово); |
Листинг 1
Короче говоря, оптимизатор SQL Server обрабатывает оба запроса одинаково, и они будут выполнять то же самое.
… или ЛЮБОЙ, ИСКЛЮЧАЯ, ВНУТРЕННЕЕ СОЕДИНЕНИЕ, ВНЕШНЕЕ СОЕДИНЕНИЕ или ПЕРЕСЕЧЕНИЕ…?
А как насчет всех других возможных методов, таких как использование ANY
, EXCEPT
, INNER
JOIN
, OUTER
JOIN
или INTERSECT
? В листинге 2 показаны еще семь альтернатив, которые я мог легко придумать, хотя будут и другие.
1 2 3 4 5 6 7 8 9 10 11 12 13 140002 13 14 18 19 20 21 22 23 24 25 26 27 28 29 30 000 000 34 35 36 37 38 39 40 41 42 43 44 45 46 47 0005 51 52 53 54 55 56 57 58 59 60 61 | — использование ЛЮБОГО SELECT Count (*) FROM dbo. WordsInDracula ГДЕ НЕТ (WordsInDracula.word = ЛЮБОЕ (ВЫБЕРИТЕ слово ИЗ общих слов)); — Правое анти-полуслитое соединение — с использованием EXCEPT SELECT Count (*) FROM ( SELECT word FROM dbo.WordsInDracula EXCEPT 9000 word dbo.CommonWords ) AS JustTheUncommonOnes; — Правое анти-полуслитое соединение —использование ЛЕВОГО ВНЕШНЕГО СОЕДИНЕНИЯ SELECT Count (*) FROM dbo.WordsInDracula ЛЕВОЕ ВНЕШНЕЕ СОЕДИНЕНИЕ dbo.CommonWords ON CommonWords.word = WordsinDracula.word ГДЕ CommonWords.word IS NULL; — правое внешнее соединение слиянием — использование ПОЛНОГО ВНЕШНЕГО СОЕДИНЕНИЯ SELECT Count (*) FROM dbo.WordsInDracula full OUTER JOIN dbo.CommonWords ON CommonWords.word.word = WordsinDracula ГДЕ CommonWords.word ЕСТЬ NULL; —Полное внешнее соединение реализовано как соединение слиянием. — использование пересечения для получения разницы SELECT (SELECT Count (*) FROM WordsInDracula) -Count (*) FROM ( SELECT word FROM dbo.WordsInDracula 000 correct ВЫБЕРИТЕ слово ИЗ dbo.CommonWords ) AS JustTheUncommonOnes; — внутреннее объединение слиянием — использование синтаксиса FULL OUTER JOIN для получения разницы SELECT Count (*) — (SELECT Count (*) FROM CommonWords) FROM dbo.WordsInDracula full OUTER JOIN dbo.CommonWords ON CommonWords.word = WordsinDracula.word — полное внешнее объединение слиянием — использование синтаксиса INNER JOIN для получения разницы SELECT (SELECT Count (*) FROM WordsinDracula) -Count (*) FROM dbo.WordsInDracula INNER JOIN dbo.CommonWords ON CommonWords.word = WordsinDracula.word — внутреннее соединение слиянием |
Листинг 2
Испытательная привязь
Все девять запросов дают одинаковые результаты, но работает ли какой-либо один подход лучше? Давайте поместим их всех в простую систему тестирования, чтобы увидеть, сколько времени занимает каждая версия! Опять же, файл загрузки кода включает в себя код тестовой оснастки и все девять запросов.
Как показывают результаты, хотя запросы выглядят по-разному, обычно это просто «синтаксический сахар» для оптимизатора. Каким бы элегантным ни был ваш SQL, оптимизатор просто пожимает плечами и предлагает эффективный план его выполнения. Фактически, первые четыре используют один и тот же план выполнения «правильное противодействие полуслияниям», и все они занимают одинаковое количество времени.
Мы проверим вариации, запустив тест несколько раз. Запросы INTERSECT
и INNER JOIN
использовали внутреннее соединение слиянием и были близки.Два запроса FULL OUTER JOIN
были немного медленнее, но это была близкая гонка.
Ловушка НЕ В
Есть определенная нереальность в сравнении наборов с нулевыми значениями в них, но если это происходит в разгар ежедневных отчетов базы данных, все может пойти очень плохо. Если у вас есть значение NULL
в результате подзапроса или выражения, которое передается логическому оператору IN
, он даст разумный ответ, такой же, как эквивалент EXISTS
. Однако НЕ В
ведет себя иначе.
Листинг 3 демонстрирует проблему. Мы вставляем три общих и три необычных слова в табличную переменную @someWord
и хотим узнать количество общих слов, которых нет в нашей табличной переменной.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 18 19 20 21 22 23 24 25 26 27 28 29 30 000 000 34 35 36 37 38 39 40 41 42 43 44 45 46 47 00050005 48 000 51 52 53 54 55 56 57 | УСТАНОВИТЬ БЕЗ СЧЕТА; DECLARE @someWord TABLE ( word NVARCHAR (35) NULL ); INSERT INTO @someWord ( word ) — три общих слова SELECT TOP 3 word FROM dbo. общие слова ORDER BY word DESC; — три необычных слова INSERT INTO @someWord ( word ) ЗНАЧЕНИЯ (‘flibberty’), (‘jibberty 9000’), b0002, ; SELECT [НЕ СУЩЕСТВУЕТ без NULL] = COUNT (*) FROM commonwords AS MyWords WHERE NOT EXISTS ( SELECT word FROM @someWord AS s WHERE s.word LIKE MyWords.слово ); SELECT [NOT IN без NULL] = COUNT (*) FROM commonwords AS MyWords WHERE word NOT IN ( SELECT word FROM @someWord ); —Вставить значение NULL INSERT INTO @someWord ( word ) VALUES (NULL); SELECT [NOT EXISTS with NULL] = COUNT (*) FROM commonwords AS MyWords WHERE NOT EXISTS ( SELECT word FROM @someWord AS s WHERE s.word LIKE MyWords.word ); SELECT [NOT IN with NULL] = COUNT (*) FROM commonwords AS MyWords WHERE word NOT IN ( SELECT word FROM @someWord ); |
Листинг 3.
Запрос NOT
IN
до того, как мы вставили NULL
в какое-то слово @
, и оба запроса NOT
EXISTS
, все правильно говорят нам, что 60385 слов нет в нашей табличной переменной, потому что три есть, а всего существует 60388 общих слов.Однако, если подзапрос может вернуть NULL
, то NOT
IN
вообще не возвращает строк.
NULL
на самом деле означает «неизвестно», а не ничего, поэтому любое выражение, которое сравнивается со значением NULL
, возвращает NULL
или unknown.
Логически SQL Server оценивает подзапрос, заменяет его списком возвращаемых значений, а затем оценивает условие [НЕ]
IN
.Для варианта нашего запроса IN
это не вызывает проблемы, поскольку решает следующее:
ГДЕ word = ‘flibberty’ OR word = ‘jibberty’ OR word = ‘flob’ OR word = ‘zygotes’ OR word = ‘zygote’ OR word = ‘zydeco’ OR word = NULL; |
Это возвращает 3 строки для совпадений по словам «z…». Жало идет с НЕ
IN
, что разрешает следующее:
ГДЕ слово <> ‘flibberty’ И слово <> ‘jibberty’ И слово <> ‘flob’ И слово <> ‘zygotes’ И слово <> ‘zygote’ И слово <> ‘zydeco’ И слово <> NULL; |
Условие И
при сравнении с NULL
оценивается как «неизвестно», поэтому выражение всегда будет возвращать нулевые строки.Это не ошибка; это по дизайну. Вы можете возразить, что NULL
нельзя допускать ни в одном столбце, где вы хотите использовать выражение NOT
IN
, но в нашей реальной рабочей жизни эти вещи могут закрасться в источники таблиц. Стоит быть осторожным. Итак, используйте вариант EXISTS
или один из других, или всегда не забывайте включать предложение WHERE
в условие IN
, чтобы исключить NULL
s.
Оператор SQL NOT EXISTS
Оператор SQL NOT EXISTS будет действовать прямо противоположно оператору EXISTS. Он используется для ограничения количества строк, возвращаемых оператором SELECT.
НЕ СУЩЕСТВУЕТ в SQL Server проверяет наличие строк в подзапросе, и, если строк нет, он вернет ИСТИНА, иначе ЛОЖЬ. Или мы можем просто сказать, что оператор SQL Server Not Exists вернет результаты, в точности противоположные результату, возвращаемому подзапросом.
Прежде чем переходить к этому примеру, я предлагаю вам обратиться к статье «Подзапросы SQL», чтобы понять, как проектировать и анализировать подзапросы.
SQL NOT EXISTS Синтаксис
Основной синтаксис NOT EXISTS в SQL Server можно записать как:
SELECT [Column Names] ОТ [Источник] ГДЕ НЕ СУЩЕСТВУЕТ (написать подзапрос для проверки)
- Столбцы: позволяет выбрать количество столбцов в таблицах. Это может быть Один или несколько.
- Источник: одна или несколько таблиц в базе данных. SQL JOINS используются для объединения нескольких таблиц.
- Подзапрос: Здесь мы должны предоставить подзапрос. Если подзапрос возвращает истину, он вернет записи, в противном случае он не вернет никаких записей.
В этой статье мы покажем вам, как использовать SQL Server NOT EXISTS Operator с примерами. Для этого мы собираемся использовать данные, показанные ниже.
SQL NOT EXISTS. Пример 1
. Следующий запрос найдет всех сотрудников, присутствующих в таблице «Сотрудники», у которых [Продажи] меньше 1000
- SQL Server НЕ EXISTS Пример ИСПОЛЬЗУЙТЕ [Учебное пособие по SQL] ИДТИ ВЫБЕРИТЕ Employ1. [EmpID] , Employ1. [FirstName] + '' + Employ1. [LastName] AS [Full Name] , Employ1.[Образование] , Employ1. [Профессия] , Employ1. [Годовой доход] , Employ1. [Продажи] , Employ1. [HireDate] ОТ [Сотрудник] КАК Сотрудник1 ГДЕ НЕ СУЩЕСТВУЕТ (ВЫБРАТЬ * ОТ [Сотрудник] КАК Employ2 ГДЕ Employ1. [EmpID] = Employ2. [EmpID] И [Продажи]> 1000 )
OUTPUT
Позвольте мне изменить условие Not Exists на Sales <10000, это означает, что подзапрос вернет все доступные строки. И НЕ СУЩЕСТВУЕТ вернет нулевые записи, потому что он вернет результат, прямо противоположный подзапросу.
- Пример SQL Server НЕ СУЩЕСТВУЕТ ИСПОЛЬЗУЙТЕ [Учебное пособие по SQL] ИДТИ ВЫБЕРИТЕ Employ1. [EmpID] , Employ1. [FirstName] + '' + Employ1. [LastName] AS [Full Name] , Employ1. [Образование] , Employ1. [Профессия] , Employ1. [Годовой доход] , Employ1. [Продажи] , Employ1. [HireDate] ОТ [Сотрудник] КАК Сотрудник1 ГДЕ НЕ СУЩЕСТВУЕТ (ВЫБРАТЬ * ОТ [Сотрудник] КАК Employ2 ГДЕ Employ1. [EmpID] = Employ2. [EmpID] И [Продажи] <10000 )
OUTPUT
Как вы можете видеть, запрос возвращает пустые записи, поскольку подзапрос возвращает TRUE, а Not exists вернет false.Позвольте нам показать вам еще один пример для лучшего понимания.
Позвольте мне изменить условие на «Продажи> 10000», что является ложным условием. Итак, оператор SQL NOT EXISTS вернет все записи.
- Пример SQL Server НЕ СУЩЕСТВУЕТ ИСПОЛЬЗУЙТЕ [Учебное пособие по SQL] ИДТИ ВЫБЕРИТЕ Employ1. [EmpID] , Employ1. [FirstName] + '' + Employ1. [LastName] AS [Full Name] , Employ1. [Образование] , Employ1. [Профессия] , Employ1. [Годовой доход] , Employ1. [Продажи] , Employ1. [HireDate] ОТ [Сотрудник] КАК Сотрудник1 ГДЕ НЕ СУЩЕСТВУЕТ (ВЫБРАТЬ * ОТ [Сотрудник] КАК Employ2 ГДЕ Работать1.[EmpID] = Employ2. [EmpID] И [Продажи]> 10000 )
ВЫХОД
Как видно из приведенного выше снимка экрана, он возвращает все строки. Поскольку подзапрос возвращает FALSE, это означает, что Sql Server NOT EXISTS вернет TRUE
SQL Not Exists Example 2
Следующий запрос SQL Server Not Exists найдет сотрудников, чья профессия не является ни квалифицированным руководителем, ни служащим. Здесь мы собираемся использовать оператор SQL IN внутри подзапроса
- пример для оператора SQL Server NOT EXISTS ИСПОЛЬЗУЙТЕ [Учебное пособие по SQL] ИДТИ ВЫБЕРИТЕ Employ1.[EmpID] , Employ1. [FirstName] + '' + Employ1. [LastName] AS [Full Name] , Employ1. [Образование] , Employ1. [Профессия] , Employ1. [Годовой доход] , Employ1. [Продажи] , Employ1. [HireDate] ОТ [Сотрудник] КАК Сотрудник1 ГДЕ НЕ СУЩЕСТВУЕТ (ВЫБРАТЬ * ОТ [Сотрудник] КАК Employ2 ГДЕ Employ1. [EmpID] = Employ2. [EmpID] И [Профессия] IN («Квалифицированное руководство», «Канцелярское дело») )
ВЫХОД
Спасибо, что посетили наш блог
ORA-00942 Сообщение об ошибке - TekStream
Ошибка Ora-00942 означает, что вы пытаетесь выполнить инструкцию SQL, которая ссылается на таблицу или представление, которые не существуют.Существует несколько возможных причин ошибки «таблица или представление не существует», в том числе:
- Ссылка на несуществующую таблицу или представление
- Использование неавторизованного синонима
- Использование выражения представления, в котором требуется таблица
- Попытка использовать таблицу без надлежащего разрешения или привилегии
При возникновении любой ошибки всегда сначала просматривайте команду oerr , чтобы увидеть подробности ошибки. Просмотрите команду oerr и следуйте приведенным ниже советам по устранению ошибки.
Посмотреть словарь данных
Чтобы проверить, существует ли таблица или представление, запросите словарь данных, чтобы просмотреть список всех существующих таблиц и представлений ( показано ниже ). Убедитесь, что имена таблиц и представлений написаны правильно, и что на представление нет ссылки там, где таблица необходима.
Выполните следующий запрос, чтобы просмотреть словарь данных:
ВЫБРАТЬ *
ИЗ all_objects
ГДЕ object_type IN («ТАБЛИЦА», «ВИД»)
И имя_объекта = «ИМЯ ОБЪЕКТА»;
Проверить, существует ли представление, таблица или синоним
Распространенной причиной ошибки Ora-00942 является то, что таблица или представление не были созданы.Если вы не уверены, существует ли представление, таблица или синоним, выполните следующий запрос:
ВЫБРАТЬ *
ИЗ DBA_TABLES
WHERE TABLE_NAME = «имя_таблицы»;
ВЫБРАТЬ *
ИЗ DBA_SYNONYM
WHERE SYNONYM_NAME = «имя_синонима»;
ВЫБРАТЬ *
ИЗ DBA_VIEWS
WHERE VIEW_NAME = ‘view_name’;
Ссылка на правильную схему
Вы можете увидеть ошибку Ora-00942, потому что вы ссылаетесь на таблицу или представление в схеме, которую вы не создавали, но которая находится в другой схеме. Чтобы правильно выполнить запрос из другой схемы, вы должны ссылаться на таблицу по имени схемы.
ВЫБРАТЬ *
ИЗ имя_схемы.имя_таблицы;
Если вы не уверены, к какой схеме принадлежит таблица или представление, выполните следующий запрос:
ВЫБЕРИТЕ собственника
ИЗ all_objects
ГДЕ object_type IN («ТАБЛИЦА», «ВИД»)
И имя_объекта = «имя_таблицы»;
Если таблица не была создана и у вас нет необходимых прав, вам нужно будет связаться с администратором базы данных.Если ни одна из вышеперечисленных причин не является проблемой, убедитесь, что соединение с базой данных настроено правильно. Хотя эта причина не так распространена, как перечисленные выше, возможно, ваш сервер не смог успешно установить соединение с базой данных. Чтобы избежать появления ошибки Ora-00942 в будущем, просмотрите базу данных и узнайте, какие таблицы и представления в какой схеме находятся перед выполнением запросов. Дважды проверьте орфографию и помните о своих правах пользователя. Консультации с партнерской фирмой Oracle - это выгодный путь к внедрению программного обеспечения Oracle в вашем бизнесе.
SELECT * и EXISTS против IN против JOINs
Довольно часто разработчики перекладывают ответственность за производительность сервера на администраторов баз данных. Но, честно говоря, вы как разработчик обязаны писать эффективный и оптимальный код. Однако при написании кода разработчику часто не хватает знаний и / или опыта работы с SQL Server для выполнения этого требования.
В этой серии статей я буду обсуждать различные аспекты проектирования запросов с учетом производительности и масштабируемости.Мы рассмотрим наиболее распространенные концепции, которые, как мы часто видим, негативно влияют на код SQL, написанный разработчиками.
Чтобы убедиться, что мы не попадаем в сферу «зависит от обстоятельств», вся статья в основном основана на правиле 80-20. 80% рабочих правил могут выиграть от обобщения, но все еще есть 20% конкретных конкретных использованных случаев, в которых обобщение может оказаться бесполезным.
В статье мы поговорим о различных операторах и о том, что они делают, когда приходят и что происходит.
Избегайте использования SELECT *
При написании запросов было бы лучше устанавливать нужные столбцы в операторе select, а не в SELECT *. Для этой рекомендации есть много причин, например:
- SELECT * Извлекает ненужные данные, кроме того, это может увеличить сетевой трафик, используемый для ваших запросов.
- Когда вы SELECT *, можно получить два столбца с одинаковым именем из двух разных таблиц (например, при использовании JOINS).
- SELECT * с условиями WHERE по умолчанию будет использовать кластерный индекс, поэтому другие оптимальные индексы могут не использоваться.
- Приложение может выйти из строя из-за изменения порядка столбцов.
Давайте попробуем проверить недостатки использования SELECT * с образцом базы данных AdventureWorks2014 :
Включите фактический план выполнения при выполнении следующего запроса:
ИСПОЛЬЗОВАТЬ AdventureWorks2014 GO УСТАНОВИТЬ СТАТИСТИКУ IO ON ВЫБРАТЬ SalesOrderID, SalesOrderDetailID, CarrierTrackingNumber ИЗ отдела продаж. SalesOrderDetail ГДЕ ProductID = 707 GO ВЫБРАТЬ * ИЗ Sales.SalesOrderDetail ГДЕ ProductID = 707 GO |
Как вы можете видеть ниже, оба запроса имеют одинаковое количество извлеченных строк и одинаковое количество выполненных логических чтений.
Хотя индекс отсутствует, для первого оператора SELECT есть небольшое преимущество в стоимости запроса.
Снимок экрана взят из ApexSQL Plan, инструмента для просмотра и анализа планов выполнения запросов SQL Server
Теперь давайте создадим недостающий индекс, как предложено оптимизатором запросов.
СОЗДАТЬ ИНДЕКС IX_SalesOrderDetail_1 ON Sales.SalesOrderDetail (ProductID, SalesOrderID, SalesOrderDetailID, CarrierTrackingNumber) |
И повторите те же запросы еще раз.
Теперь разница очень очевидна: первый запрос имеет только 13 логических операций чтения с относительной стоимостью запроса всего 1% по сравнению со вторым запросом, который фактически выполняет полное сканирование кластеризованного индекса, не используя недавно созданный некластеризованный индекс. индекс.
СУЩЕСТВУЕТ против IN против СОЕДИНЕНИЙ
Прежде чем выбрать IN или EXISTS, вам нужно рассмотреть некоторые детали.
В большинстве случаев IN и EXISTS дают одинаковые результаты с одинаковой производительностью.
С другой стороны, при использовании JOINS вы можете не получить тот же набор результатов, что и в предложениях IN и EXISTS.
Итак, чтобы оптимизировать производительность, вам нужно уметь использовать и выбирать, какой из операторов.
1. EXISTS vs IN vs JOIN с столбцами NOT NULLable:
Мы будем использовать базу данных TEMPDB для всех этих сценариев.
Следующий сценарий создаст и заполнит две таблицы в базе данных TEMPDB. Основные идеи в этих таблицах заключаются в том, что маленькая таблица является подмножеством большой таблицы, а столбец идентификатора не допускает пустых значений.
1 2 3 4 5 6 7 8 9 10 11 12 13 140002 13 14 18 19 20 21 22 23 24 25 26 27 28 29 30 |
ИСПОЛЬЗОВАТЬ tempdb GO УСТАНОВИТЬ NOCOUNT ON GO СОЗДАТЬ ТАБЛИЦУ BigTable (ID INT NOT NULL, Имя VARCHAR (100), Фамилия VARCHAR (1000002), CHAR )GO
INSERT INTO BigTable (ID, FirstName, LastName, City) SELECT TOP 100000 ROW_NUMBER () OVER (ORDER BY a. name) RowID, 'Bob', CASE WHEN ROW_NUMBER () OVER (ORDER BY a.name)% 2 = 1 THEN 'Smith' ELSE 'Brown' END, CASE WHEN ROW_NUMBER () OVER ( ЗАКАЗАТЬ ПО a.name)% 10 = 1 ТОГДА 'Нью-Йорк' КОГДА НОМЕР СТРОКИ () ВЫШЕ (ЗАКАЗАТЬ НА имя)% 10 = 5 ТОГДА 'Сан-Марино' КОГДА НОМЕР СТРОКИ () ВЫШЕ (ЗАКАЗАТЬ НА. name)% 10 = 3 ТОГДА 'Лос-Анджелес' КОГДА ROW_NUMBER () ВЫШЕ (ЗАКАЗАТЬ ПО имени)% 427 = 1 ТОГДА 'Хайдарабад' ИНАЧЕ 'Хьюстон' КОНЕЦ ОТ sys.all_objects a CROSS JOIN sys.all_objects b GO CREATE TABLE SmallTable (ID INT NOT NULL, FirstName VARCHAR (100), LastName VARCHAR (100), 1002 City VARCH) GO INSERT INTO SmallTable (ID, FirstName, LastName, City) SELECT TOP (1000) * FROM BigTable |
СОЗДАТЬ КЛАСТЕРНЫЙ ИНДЕКС IX_BigTable_ID НА BigTable (ID) GO СОЗДАТЬ КЛАСТЕРИРОВАННЫЙ ИНДЕКС IX_SmallTable_ID НА SmallTable (ID) GO |
Наконец, мы добавим одну повторяющуюся строку в небольшую таблицу:
ВСТАВИТЬ В SmallTable (ID, FirstName, LastName, City) SELECT TOP (1) * FROM SmallTable |
Теперь, включив фактический план выполнения, выполните следующие три запроса вместе, чтобы выяснить различия:
1 2 3 4 5 6 7 8 9 10 11 12 13 140002 13 14 18 19 20 21 22 |
УСТАНОВИТЬ СТАТИСТИКУ IO ON SET NOCOUNT OFF print'Using IN Clause ' SELECT ID, City FROM BigTable WHERE ID IN (SELECT ID FROMFROM Small FROM Small SELECT ID, City FROM BigTable WHERE EXISTS (SELECT ID FROM SmallTable WHERE SmallTable. ID = BigTable.ID) GO Печать 'Using JOIN' SELECT bt.ID, bt.City FROM BigTable bt INNER JOIN SmallTable st ON bt.ID = st.ID GO |
Первое отличие, которое вы заметите, как я уже сказал, строки, возвращаемые JOIN, составляют 1001 строку против 1000 строк для предложений IN и EXISTS.
Причина в том, что мы вставили повторяющуюся строку.
Если мы посмотрим на планы выполнения, мы заметим, что у них одинаковая стоимость запроса - 33%.
Единственное отличие здесь состоит в том, что план выполнения запроса JOIN немного отличается, но стоимость, похоже, такая же.
В этом конкретном случае вы видите, что планы выполнения для предложений IN и EXISTS идентичны.
2. НЕ СУЩЕСТВУЕТ по сравнению с НЕ В ИЛИ СОЕДИНЕНИЕ с столбцами NOT NULLable:
Используя те же две таблицы в предыдущем сценарии и включая фактический план выполнения, выполните следующие три запроса:
1 2 3 4 5 6 7 8 9 10 11 12 13
14 18 19 20 21 22 23 |
SET STATISTICS IO ON SET NOCOUNT OFF print'Using NOT IN Clause ' SELECT ID, City FROM BigTable WHERE ID NOT IN (SELECT ID from SmallTable)print 'Использование NOT Exists Clause' SELECT ID, City FROM BigTable WHERE NOT EXISTS (SELECT ID FROM SmallTable WHERE SmallTable. ID = BigTable.ID) GO Распечатать 'Using LEFT JOIN' SELECT bt.ID, bt.City FROM BigTable bt LEFT JOIN SmallTable st ON bt.ID = st.ID WHERE .ID IS NULL GO | .
Здесь одинаковое количество записей было возвращено для всех трех запросов, но если мы посмотрим на планы выполнения на следующем рисунке (см. Ниже), можно заметить несколько иное поведение.
НЕ СУЩЕСТВУЕТ и НЕ В, дает мне 28% относительной стоимости.
И здесь я вижу, что условия JOIN на самом деле более понятны с точки зрения затрат на 43% по сравнению с партией.
Это действительно интересный случай. Итак, не могли бы вы предсказать, что произойдет, если мы переключим таблицы в предыдущем запросе?
Давайте проверим это с помощью следующего запроса:
1 2 3 4 5 6 7 8 9 10 11 12 13
14 18 19 20 21 22 23 |
SET STATISTICS IO ON SET NOCOUNT OFF print'Using NOT IN Clause ' SELECT ID, City FROM SmallTable WHERE ID NOT IN (SELECT ID FROM GO)print 'Использование NOT Exists Clause' SELECT ID, City FROM SmallTable WHERE NOT EXISTS (SELECT ID FROM BigTable WHERE SmallTable. ID = BigTable.ID) GO Распечатать 'Using LEFT JOIN' SELECT bt.ID, bt.City FROM SmallTable st LEFT JOIN BigTable bt ON bt.ID = st.ID WHERE .ID IS NULL GO | .
Понимая, что маленькая таблица является подмножеством большой таблицы, ни один из этих запросов не вернет вам какой-либо набор результатов.
Но как насчет планов выполнения? Возвращают ли они то же самое?
Интересные результаты… Теперь первый и второй запрос составляют 50% относительно пакета.
И по иронии судьбы последний запрос, который является LEFT JOIN, на самом деле показывает 0% относительного процента и выполняет постоянное сканирование.
В этом сила оптимизатора, основанного на затратах. SQL Server достаточно умен, чтобы принимать решения в соответствии с честностью, принятой за кулисами.
Следовательно, 0 строк и постоянное сканирование, что означает, что SQL Server также не коснулся большой таблицы.
Здесь на помощь приходит оптимизатор, основанный на затратах, и выполняет оптимизацию за вас, а не мы, основываясь на предложениях NOT EXISTS или NOT IN.
3. СУЩЕСТВУЕТ против IN против СОЕДИНЕНИЯ с NULLable столбцами:
После создания тех же двух таблиц, но со столбцом идентификатора, допускающим нулевое значение и заполнением их теми же данными.
Одна небольшая вещь, которую следует упомянуть здесь, чтобы убедиться, что все сценарии охвачены, заключается в том, что EXISTS vs IN vs JOIN с столбцами NULLable дадут вам те же результаты и ту же производительность, что и у столбцов NOT NULLABLE, упомянутых выше.
4.НЕ СУЩЕСТВУЕТ против НЕ В против СОЕДИНЕНИЕ с NULLable столбцами:
Мы увидим, как небольшое изменение, такое как разрешение пустых значений для столбца ID в обеих таблицах, сильно повлияет на производительность трех предложений.
Включив фактический план выполнения, выполните следующий запрос:
1 2 3 4 5 6 7 8 9 10 11 12 13
14 18 19 20 21 22 23 |
SET STATISTICS IO ON SET NOCOUNT OFF print'Using NOT IN Clause ' SELECT ID, City FROM BigTable WHERE ID NOT IN (SELECT ID from SmallTable)print 'Использование NOT Exists Clause' SELECT ID, City FROM BigTable WHERE NOT EXISTS (SELECT ID FROM SmallTable WHERE SmallTable. ID = BigTable.ID) GO Распечатать 'Using LEFT JOIN' SELECT bt.ID, bt.City FROM BigTable bt LEFT JOIN SmallTable st ON bt.ID = st.ID WHERE .ID IS NULL GO | .
Конечно, вы получите одинаковое количество записей для каждой
Но давайте посмотрим на планы выполнения:
В этом конкретном случае есть столбец NULLable.
И здесь NOT IN фактически получает все значения из таблицы. Вот почему NOT IN намного дороже. Даже когда вы переключаете таблицы, запрос NOT IN по-прежнему будет самым дорогостоящим.
Сводка
Я представил здесь несколько аспектов дизайна запроса для повышения производительности. Я пробовал несколько возможных сценариев, с которыми вы можете столкнуться при создании SQL-запросов как разработчик. Надеюсь, эта статья была для вас информативной.
Я сертифицированный эксперт по решениям Microsoft®: Data Management Analytics Plus Microsoft® Certified Solutions Expert Data Platform (MCSE). Для получения дополнительной информации проверьте это здесь.
Живя в Египте, работал старшим администратором базы данных Microsoft SQL Server более 4 лет.
Как администратор баз данных, я проектирую, устанавливаю, обслуживаю и обновляю все базы данных (производственные и непроизводственные среды), у меня есть практические знания о производительности T-SQL, проблемах производительности HW, репликации SQL Server, решениях для кластеризации и проектах баз данных для разные виды систем.Я работал со всеми версиями SQL Server (2008, 2008R2, 2012, 2014 и 2016).
Я люблю свою работу, потому что сейчас база данных - самая ценная вещь в любом месте в мире. Вот почему я не перестану учиться. В свободное время я люблю читать, говорить, узнавать что-то новое, писать блоги и статьи.
Посмотреть все сообщения от Ayman Elnory
Последние сообщения от Ayman Elnory (посмотреть все)SQL EXISTS - SQL
На языке SQL, команда EXISTS используется в соответствии с условием для обеспечения безопасности и отсутствия необходимости в использовании по запросу.
Примечание: cette commande n’est pas à confondre avec la clause IN. La commande EXISTS vérifie si la sous-Requête retourne un resultat ou non, tandis que IN vérifie la concordance d’une à plusieurs données.
Синтаксис
Базовое использование команды EXISTS состоит из проверки и возврата результатов, а также использования EXISTS в соответствии с условием. La Requête externe s’exécutera uniquement si la Requête interne retourne au moins un résultat.
ВЫБРАТЬ nom_colonne1 ОТ `table1` ГДЕ СУЩЕСТВУЕТ ( ВЫБРАТЬ nom_colonne2 ИЗ `table2` ГДЕ nom_colonne3 = 10 )
Dans l’exemple ci-dessus, s’il y a au moins une ligne dans table2 dont nom_colonne3 contient la valeur 10, alors la sous-Requête retournera au moins un résultat. Dès lors, la condition sera vérifiée et la Requête Principale Retournera les résultats de la colonne nom_colonne1 de table1 .
Exemple
Dans le but de montrer un example concret d’application, Imminons un système composé d’une table qui contient des commandes et d’une table content des produits.
Таблица commande:
c_id | c_date_achat | c_produit_id | c_quantite_produit | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
2014-01 900-08 2014-01 -01-24 | 3 | 2 | ||||||||||||
3 | 2014-02-14 | 8 | 1 | |||||||||||
4 | 2014-03-23 | 10 | 1 |
Продукт стола:
p_id | p_nom | p_date_ajout | p_prix |
---|---|---|---|
2 | Ordinateur | 2013 799-11-17 | |
3 | Клавир | 2013-11-27 | 49,9 |
4 | Souris | 2013-12-04 | 15 |
5 | Ecran | 2013-12 -15 | 250 |
Это самый возможный эффект, который не требуется, чтобы SQL использовался для выполнения команд для повышения эффективности продукта. Cette Requête peut être interprétée de la façon suivante:
SELECT * ОТ КОМАНДА ГДЕ СУЩЕСТВУЕТ ( ВЫБРАТЬ * ИЗ ПРОДУКТА ГДЕ c_produit_id = p_id )
Результат:
c_id | c_date_achat | c_produit_id | c_quantite_produit |
---|---|---|---|
-08 1 95 | |||
-08 1 | 2014 -01-24 | 3 | 2 |
Le résultat démontre bien que seul les commandes n ° 1 и n ° 2 ont un produit qui se trove dans la table produit (см.la условие c_produit_id = p_id). Cette Requête est intéressante sachant qu’elle n’influence pas le résultat de la Requête Principalale, противопоставление использованию соединения, которое является соединением столбцов двух соединенных таблиц.
Как использовать предложение EXISTS в SQL? Пример реального SQL-запроса с использованием EXISTS
Здравствуйте, ребята, вы могли слышать о том, насколько полезно предложение EXISTS при написании сложных запросов. Тем не менее, в то же время я также видел, что многие программисты изо всех сил пытаются понять и использовать предложения EXISTS и NOT EXISTS при написании SQL-запросов.Если вы один из них, то вы попали в нужное место. Сегодня вы узнаете, как использовать предложение EXISTS в SQL, взяв реальный пример и превосходный
из LeetCode. Предположим, что на веб-сайте есть две таблицы, таблица
.
Чтобы решить эту проблему, вам нужно сначала подумать о данных. Например, если клиент никогда ничего не заказывал, значит, для него не будет никакой записи в таблице «Заказы». Молодец, половина задачи сделана.
Теперь вторая половина: как проверить, есть ли запись для конкретного клиента? Здесь вам помогут предложения EXISTS и NOT EXISTS, и об этом вы узнаете из этой статьи.
На самом деле это тоже популярный вопрос для собеседований по SQL, и вы, возможно, уже видели его, но если нет, то ничего страшного. Этот вопрос не так популярен, как второй по величине запрос о заработной плате, но он также является одним из часто задаваемых SQL-запросов на собеседовании по программированию; вы можете увидеть полный список здесь.
Хотя я согласен с тем, что эту проблему можно решить другим способом, но это также прекрасный пример того, как вы можете использовать предложение SQL EXISTS .
Но, если вы новичок в мире SQL, лучше начать с всеобъемлющего курса SQL, такого как курс The Complete SQL Bootcamp от Jose Portilla на Udemy.Это поможет вам изучать SQL лучше и быстрее, и такие статьи также будут иметь больше смысла, если у вас есть некоторые знания SQL за плечами.
Таблицы и данные SQL
Прежде чем рассматривать запрос, давайте посмотрим на схему, таблицы и данные, чтобы лучше понять проблему. У нас есть две таблицы «Клиенты» и «Заказы». Клиенты содержат два столбца: Id и Name. Идентификатор - это числовой столбец, а Имя - текстовый столбец, допустим, тип VARCHAR.
Если какой-либо клиент заказал что-либо, его идентификатор клиента будет присутствовать в таблице заказов, и мы воспользуемся этим знанием, чтобы решить эту проблему.
У нас есть четыре клиента с идентификатором от 1 до 4. Наша вторая таблица, Orders, содержит идентификатор, который является уникальным идентификатором для заказа, и CustomerId, который является идентификатором клиента, который делает этот заказ. Если какой-либо Клиент разместит заказ, его идентификатор будет присутствовать в таблице «Заказы».
Таблица: Клиенты.
+ ---- + ------- +
| Id | Имя |
+ ---- + ------- +
| 1 | Джо |
| 2 | Генри |
| 3 | Сэм |
| 4 | Макс |
+ ---- + ------- +
Таблица: Заказы.
+ ---- + ------------ +
| Id | CustomerId |
+ ---- + ------------ +
| 1 | 3 |
| 2 | 1 |
+ ---- + ------------ +
Используя приведенные выше таблицы в качестве примера, верните следующее:
+ ----------- +
| Заказчики |
+ ----------- +
| Генри |
| Макс |
+ ----------- +
Если вы ищете еще несколько SQL-задач, вы можете попробовать решить задачи, приведенные в классической книге Джо Селко « SQL-головоломки и ответы», 2-е издание .Одна из лучших книг для улучшения ваших навыков работы с SQL-запросами.
Решение для клиентов, которые никогда не заказывают
Одним из наиболее распространенных решений этой проблемы является использование предложения SQL JOIN. Вы можете использовать LEFT OUTER JOIN для решения этой проблемы, как показано ниже:
SELECT C.Name FROM Customers C LEFT JOIN Orders O ON C.Id = O.CustomerId ГДЕ O.CustomerId - ПУСТО (NULL)
Когда вы соединяете две таблицы в SQL с помощью LEFT OUTER JOIN, тогда будет создана большая таблица со значениями NULL в столбце, которых нет в другой таблице.
Например, большая таблица будет иметь четыре столбца C.Id, C.Name, O.Id и O.CustomerId, для клиентов, которые никогда ничего не заказывали, O.CustomerId будет NULL.
Многие программисты совершают ошибку, используя ! = в условии JOIN для решения этой проблемы, предполагая, что if = возвращает совпадающие строки, тогда! = Вернет те идентификаторы, которых нет в другой таблице. Так что остерегайтесь этого.
Если вам сложно понять, как присоединиться, то я предлагаю вам взглянуть на отличный курс Хосе Портиллы по SQL The Complete SQL Bootcamp на Udemy.Его стиль преподавания потрясающий, и вы сразу поймете, как он присоединяется.
В любом случае, эта проблема на самом деле является отличным примером , как и когда использовать предложение EXISTS:
SELECT C.Name FROM Customers C ГДЕ НЕ СУЩЕСТВУЕТ (ВЫБЕРИТЕ 1 ИЗ Orders O WHERE C.Id = O.CustomerId)
Это коррелированный подзапрос, в котором внутренний запрос будет выполняться для каждой строки внешнего запроса, и будут возвращены только те клиенты, которые ничего не заказывали.
Кстати, самое простое решение - использовать NOT IN Clause.
ВЫБЕРИТЕ A. Имя от клиентов A ГДЕ A.Id НЕ ВХОДИТ (ВЫБЕРИТЕ B.CustomerId из заказов B)
Вот и все, что касается , как использовать предложение EXISTS в SQL для поиска всех клиентов, которые никогда не заказывали. Если вам нравится совершенствовать навыки работы с SQL-запросами, вы также можете решить задачи, описанные в классической книге Джо Селко «Загадки и ответы на SQL», 2-е издание. Одна из лучших книг с множеством сложных вопросов для проверки ваших навыков SQL, и если вам нужны онлайн-курсы для углубленного изучения SQL или заполнения пробелов в ваших знаниях SQL, следующие курсы - хорошее место для начала.
Дальнейшее обучение
Введение в SQL
SQL для новичков: анализ данных для начинающих
SQL-головоломки и ответы, 2-е издание
Другие связанные запросы SQL , вопросы для интервью и статьи:
- Как присоединиться к трем таблицы в одном SQL-запросе (решение)
- Напишите запрос SQL, чтобы найти все имена таблиц в базе данных в MySQL (решение)
- 5 курсов для изучения баз данных и SQL Better (курсы)
- Написать SQL-запрос для копирования или резервного копирования таблицы в MySQL (решение)
- Как найти повторяющиеся строки в таблице базы данных? (решение)
- 5 курсов для изучения баз данных Oracle и Microsoft SQL Server (курсы)
- Реальная разница между предложениями WHERE и HAVING в SQL? (ответ)
- Как перенести SQL-запросы с Oracle на SQL Server 2008? (ответ)
- 5 лучших веб-сайтов для БЕСПЛАТНОГО изучения SQL в Интернете? (ресурс)
- В чем разница между UNION и UNION ALL в SQL? (ответ)
- Разница между Self и Equi Join в SQL? (ответ)
- Лучшие курсы 5 для изучения базы данных MySQL для начинающих (курсы)
- В чем разница между представлением и материализованным представлением в базе данных? (ответ)
- Разница между кластеризованным и некластеризованным индексом в SQL? (ответ)
- Разница между первичным ключом и ключом кандидата в таблице? (ответ)
- 5 бесплатных курсов по изучению T-SQL и SQL Server для начинающих (курсы)
- Разница между уникальным и первичным ключом в таблице? (ответ)
- Разница между первичным и внешним ключом в таблице? (ответ)
Спасибо, что прочитали эту статью, если вам понравилась эта статья по SQL, поделитесь ею с друзьями и коллегами.