Как работают операторы SQL EXISTS?

Я пытаюсь изучить SQL, и я с трудом понимаю инструкции EXISTS. Я наткнулся на эту цитату о "существует" и ничего не понимаю:

Используя оператор exist, ваш подзапрос может возвращать ноль, один или несколько строк, а условие просто проверяет, вернул ли подзапрос какие-либо строки. Если вы посмотрите на предложение select в подзапросе, вы увидите, что он состоит из одного литерала (1); поскольку условие в содержащем запросе требует только знать, сколько строк было возвращено, фактические данные, полученные в подзапрос, не имеют значения.

Я не понимаю, как внешний запрос знает, какую строку подзапрос проверяет? Например:

SELECT *
  FROM suppliers
 WHERE EXISTS (select *
                 from orders
                where suppliers.supplier_id = orders.supplier_id);

Я понимаю, что если идентификатор из таблицы поставщика и заказов совпадает, подзапрос вернет значение true, и все столбцы из соответствующей строки в таблице поставщиков будут выведены. То, что я не получаю, - это то, как подзапрос сообщает, какая конкретная строка (скажем, строка с идентификатором поставщика 25) должна быть напечатана, если возвращается только true или false.

Мне кажется, что между внешним запросом и подзапросом нет никакой связи.

Ответ 1

Подумайте об этом так:

Для строки "каждая" из Suppliers проверьте, существует ли строка в таблице Order, соответствующая условию Suppliers.supplier_id (это происходит из строки "Внешний запрос" ) = Orders.supplier_id. Когда вы найдете первую совпадающую строку, остановитесь прямо там - WHERE EXISTS выполнено.

Магическая связь между внешним запросом и подзапросом заключается в том, что Supplier_id передается из внешнего запроса в подзапрос для каждой оцениваемой строки.

Или, другими словами, подзапрос выполняется для каждой строки таблицы внешнего запроса.

Это НЕ похоже, что подзапрос выполняется в целом и получает "true/false", а затем пытается сопоставить это условие "true/false" с внешним запросом.

Ответ 2

Мне кажется, что между внешним запросом и подзапросом нет никакой связи.

Как вы думаете, что делает предложение WHERE в примере EXISTS? Как вы пришли к такому выводу, когда ссылка SUPPLIERS не содержится в предложениях FROM или JOIN в предложении EXISTS?

EXISTS оценивает значение TRUE/FALSE и выходит из TRUE в первом совпадении критериев - поэтому он может быть быстрее, чем 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, потому что будут дублированные родительские записи, если имеется более одной дочерней записи, связанной с родителем.

Ответ 3

Вы можете создавать идентичные результаты с помощью 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

Ответ 4

Если у вас есть предложение where, которое выглядит так:

WHERE id in (25,26,27) -- and so on

вы можете легко понять, почему некоторые строки возвращаются, а некоторые нет.

Когда предложение where выглядит так:

WHERE EXISTS (select * from orders where suppliers.supplier_id = orders.supplier_id);

это просто означает: вернуть строки, которые имеют существующую запись в таблице заказов с тем же идентификатором.

Ответ 5

EXISTS означает, что в подзапросе возвращается хотя бы одна строка, что и есть на самом деле. В этом случае это коррелированный подзапрос, потому что он проверяет поставщик_ид внешней таблицы на имя поставщика_ид внутренней таблицы. Этот запрос говорит, по сути:

ВЫБРАТЬ всех поставщиков Для каждого идентификатора поставщика см., Существует ли заказ для этого поставщика Если поставщик отсутствует в таблице заказов, удалите поставщика из результатов ВОЗВРАТИТЕ всех поставщиков, у которых есть соответствующие строки в таблице заказов.

Вы можете сделать то же самое в этом случае с INNER JOIN.

SELECT suppliers.* 
  FROM suppliers 
 INNER 
  JOIN orders 
    ON suppliers.supplier_id = orders.supplier_id;

Комментарий Ponies правильный. Вам нужно будет группировать с этим соединением или выбрать отдельный в зависимости от данных, которые вам нужны.

Ответ 6

То, что вы описываете, - это так называемый запрос с коррелированный подзапрос.

(В общем) это то, чего вы должны избегать, написав запрос, используя вместо этого соединение:

SELECT suppliers.* 
FROM suppliers 
JOIN orders USING supplier_id
GROUP BY suppliers.supplier_id

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