Я пытаюсь изучить SQL, и мне трудно понять операторы EXISTS. Я наткнулся на эту цитату о "существует" и чего-то не понимаю:
Используя оператор exists, ваш подзапрос может вернуть ноль, одну или несколько строк, а условие просто проверяет, вернул ли подзапрос какие-либо строки. Если вы посмотрите на предложение select подзапроса, вы увидите, что он состоит из одного литерала (1); поскольку условию в содержащем запросе нужно только знать, сколько строк было возвращено, фактические данные, возвращенные подзапросом, не имеют значения.
Я не понимаю, как внешний запрос узнает, какую строку проверяет подзапрос? Например:
SELECT *
FROM suppliers
WHERE EXISTS (select *
from orders
where suppliers.supplier_id = orders.supplier_id);
Я понимаю, что если идентификатор из таблицы поставщиков и заказов совпадает, подзапрос вернет истину, и будут выведены все столбцы из соответствующей строки в таблице поставщиков. Я не понимаю, как подзапрос сообщает, какая конкретная строка (скажем, строка с идентификатором поставщика 25) должна быть напечатана, если возвращается только истина или ложь.
Мне кажется, что нет никакой связи между внешним запросом и подзапросом.
Как вы думаете, что делает предложение WHERE в примере EXISTS? Как вы пришли к такому выводу, если ссылки SUPPLIERS нет в предложениях FROM или JOIN в предложении EXISTS?
EXISTS оценивает значение ИСТИНА / ЛОЖЬ и завершает работу как ИСТИНА при первом совпадении критериев - поэтому может быть быстрее, чем
IN
. Также имейте в виду, что предложение SELECT в EXISTS игнорируется - IE:SELECT s.* FROM SUPPLIERS s WHERE EXISTS (SELECT 1/0 FROM ORDERS o WHERE o.supplier_id = s.supplier_id)
... должен достичь ошибки деления на ноль, но этого не произойдет. Предложение WHERE - самая важная часть предложения EXISTS.
Также имейте в виду, что JOIN не является прямой заменой EXISTS, потому что будут дублироваться родительские записи, если с родителем связано более одной дочерней записи.
источник
EXISTS
выходы, возвращающие TRUE при первом совпадении - потому что поставщик существует хотя бы один раз в таблице ORDERS. Если вы хотите увидеть дублирование данных SUPPLIER из-за наличия более одного дочернего отношения в ORDERS, вам придется использовать JOIN. Но большинство из них не хотят этого дублирования, и выполнение GROUP BY / DISTINCT может увеличить накладные расходы на запрос.EXISTS
более эффективен, чемSELECT DISTINCT ... FROM SUPPLIERS JOIN ORDERS ...
на SQL Server, в последнее время не тестировался на Oracle или MySQL.Вы можете производить одинаковые результаты , используя либо
JOIN
,EXISTS
,IN
илиINTERSECT
:SELECT s.supplier_id FROM suppliers s INNER JOIN (SELECT DISTINCT o.supplier_id FROM orders o) o ON o.supplier_id = s.supplier_id SELECT s.supplier_id FROM suppliers s WHERE EXISTS (SELECT * FROM orders o WHERE o.supplier_id = s.supplier_id) SELECT s.supplier_id FROM suppliers s WHERE s.supplier_id IN (SELECT o.supplier_id FROM orders o) SELECT s.supplier_id FROM suppliers s INTERSECT SELECT o.supplier_id FROM orders o
источник
Если бы у вас было предложение where, которое выглядело так:
WHERE id in (25,26,27) -- and so on
вы можете легко понять, почему одни строки возвращаются, а некоторые нет.
Когда предложение where выглядит так:
WHERE EXISTS (select * from orders where suppliers.supplier_id = orders.supplier_id);
это просто означает: вернуть строки, которые имеют существующую запись в таблице заказов с тем же идентификатором.
источник
Модель таблицы базы данных
Предположим, у нас есть следующие две таблицы в нашей базе данных, которые формируют отношение таблиц «один ко многим».
student
Таблица является родителем, иstudent_grade
является дочерней таблицей , так как он имеет student_id столбец внешнего ключа , ссылающийся Ид столбец первичного ключа в таблице студента.student table
Содержит следующие две записи:| id | first_name | last_name | admission_score | |----|------------|-----------|-----------------| | 1 | Alice | Smith | 8.95 | | 2 | Bob | Johnson | 8.75 |
А в
student_grade
таблице хранятся оценки, полученные учениками:| id | class_name | grade | student_id | |----|------------|-------|------------| | 1 | Math | 10 | 1 | | 2 | Math | 9.5 | 1 | | 3 | Math | 9.75 | 1 | | 4 | Science | 9.5 | 1 | | 5 | Science | 9 | 1 | | 6 | Science | 9.25 | 1 | | 7 | Math | 8.5 | 2 | | 8 | Math | 9.5 | 2 | | 9 | Math | 9 | 2 | | 10 | Science | 10 | 2 | | 11 | Science | 9.4 | 2 |
SQL СУЩЕСТВУЕТ
Допустим, мы хотим получить всех учеников, получивших 10 баллов по математике.
Если нас интересует только идентификатор студента, мы можем выполнить такой запрос:
SELECT student_grade.student_id FROM student_grade WHERE student_grade.grade = 10 AND student_grade.class_name = 'Math' ORDER BY student_grade.student_id
Но приложение заинтересовано в отображении полного имени, а
student
не только идентификатора, поэтому нам также нужна информация изstudent
таблицы.Чтобы отфильтровать
student
записи с оценкой 10 по математике, мы можем использовать оператор EXISTS SQL, например:SELECT id, first_name, last_name FROM student WHERE EXISTS ( SELECT 1 FROM student_grade WHERE student_grade.student_id = student.id AND student_grade.grade = 10 AND student_grade.class_name = 'Math' ) ORDER BY id
При выполнении запроса выше мы видим, что выбрана только строка Алисы:
| id | first_name | last_name | |----|------------|-----------| | 1 | Alice | Smith |
Внешний запрос выбирает
student
столбцы строк, которые мы хотим вернуть клиенту. Однако предложение WHERE использует оператор EXISTS со связанным внутренним подзапросом.Оператор EXISTS возвращает true, если подзапрос возвращает хотя бы одну запись, и false, если строка не выбрана. Механизм базы данных не должен полностью выполнять подзапрос. Если совпадает одна запись, оператор EXISTS возвращает true, и выбирается связанная другая строка запроса.
Внутренний подзапрос коррелирован, потому что столбец student_id
student_grade
таблицы сопоставляется со столбцом id внешней таблицы student.источник
EXIST
только с коррелированным подзапросом? Я играл с запросом, содержащим только одну таблицу, напримерSELECT id FROM student WHERE EXISTS (SELECT 1 FROM student WHERE student.id > 1)
. Я знаю, что то, что я написал, может быть достигнуто одним простым запросом WHERE, но я просто использовал его, чтобы понять EXISTS. Я получил все строки. Действительно ли это связано с тем, что я не использовал коррелированный подзапрос? Спасибо.EXISTS означает, что подзапрос возвращает хотя бы одну строку, это действительно так. В этом случае это коррелированный подзапрос, поскольку он проверяет поставщик_id внешней таблицы на поставщик_ид внутренней таблицы. Этот запрос фактически говорит:
ВЫБЕРИТЕ всех поставщиков. Для каждого идентификатора поставщика проверьте, существует ли заказ для этого поставщика. Если поставщик отсутствует в таблице заказов, удалите его из результатов.ВЕРНУТЬ всех поставщиков, у которых есть соответствующие строки в таблице заказов.
В этом случае вы можете сделать то же самое с INNER JOIN.
SELECT suppliers.* FROM suppliers INNER JOIN orders ON suppliers.supplier_id = orders.supplier_id;
Комментарий пони правильный. Вам нужно будет выполнить группировку с этим объединением или выбрать отдельные в зависимости от того, какие данные вам нужны.
источник
Вы описываете так называемый запрос с коррелированным подзапросом .
(В общем) это то, чего вы должны избегать, написав запрос, используя вместо этого соединение:
SELECT suppliers.* FROM suppliers JOIN orders USING supplier_id GROUP BY suppliers.supplier_id
В противном случае подзапрос будет выполняться для каждой строки внешнего запроса.
источник
orders
которая соответствует условию соединения.