DBAtlas · Атлас эксплуатации баз данных

EXISTS / IN / NOT EXISTS (подзапрос)

Oracle

-- Customers with orders (EXISTS)
SELECT name FROM customer m
WHERE EXISTS (SELECT 1 FROM orders s WHERE s.customer_id = m.id);

-- List membership with IN
SELECT name FROM customer
WHERE city IN ('Ankara', 'Izmir');

-- Customers with no orders at all (NOT EXISTS)
SELECT name FROM customer m
WHERE NOT EXISTS (SELECT 1 FROM orders s WHERE s.customer_id = m.id);

EXISTS проверяет, возвращает ли коррелированный подзапрос хотя бы одну строку (останавливается на первом совпадении, поэтому быстр). IN проверяет, входит ли значение в список/результат подзапроса. Осторожно: если подзапрос может вернуть NULL, NOT IN даёт неожиданные результаты; NOT EXISTS надёжнее. Оптимизатор Oracle часто превращает оба в один и тот же план.

PostgreSQL

SELECT name FROM customer m
WHERE EXISTS (SELECT 1 FROM orders s WHERE s.customer_id = m.id);

SELECT name FROM customer
WHERE city IN (SELECT city FROM target_cities);

SELECT name FROM customer m
WHERE NOT EXISTS (SELECT 1 FROM orders s WHERE s.customer_id = m.id);

PostgreSQL полностью поддерживает EXISTS/IN/NOT EXISTS. С NOT IN, если подзапрос содержит NULL, весь результат может оказаться пустым, поэтому рекомендуются NOT EXISTS или LEFT JOIN ... IS NULL. Планировщик в большинстве случаев обрабатывает IN и EXISTS одинаково, как semi-join.

MySQL

SELECT name FROM customer m
WHERE EXISTS (SELECT 1 FROM orders s WHERE s.customer_id = m.id);

SELECT name FROM customer
WHERE city IN ('Ankara', 'Izmir');

SELECT name FROM customer m
WHERE NOT EXISTS (SELECT 1 FROM orders s WHERE s.customer_id = m.id);

MySQL/MariaDB поддерживают EXISTS/IN/NOT EXISTS. До MySQL 5.6 коррелированные подзапросы могли быть медленными; 5.6+ ускорил подзапросы IN оптимизациями semi-join и материализации. Ловушка NOT IN + NULL действует и здесь.

SQL Server

SELECT name FROM customer m
WHERE EXISTS (SELECT 1 FROM orders s WHERE s.customer_id = m.id);

SELECT name FROM customer
WHERE city IN (SELECT city FROM target_cities);

SELECT name FROM customer m
WHERE NOT EXISTS (SELECT 1 FROM orders s WHERE s.customer_id = m.id);

SQL Server поддерживает EXISTS/IN/NOT EXISTS, и оптимизатор в основном обрабатывает их как semi/anti-join. Если подзапрос NOT IN содержит NULL, результат может неожиданно оказаться пустым; NOT EXISTS — безопасный выбор. Для похожей логики принадлежности можно использовать и INTERSECT/EXCEPT.

MongoDB

// IN equivalent: the $in operator
db.customer.find({ city: { $in: ["Ankara", "Izmir"] } })

// EXISTS equivalent: match check with $lookup + $match
db.customer.aggregate([
  { $lookup: { from: "orders", localField: "id",
      foreignField: "customer_id", as: "s" } },
  { $match: { "s.0": { $exists: true } } }  // at least one order
])

В MongoDB принадлежность списку значений проверяется операторами $in / $nin. Эквивалент EXISTS с подзапросом — получить массив совпадающих документов через $lookup и фильтровать по тому, пуст ли массив. Проверка наличия поля — отдельный оператор { field: { $exists: true } } (наличие поля, а не связь строк).

Redis

# Membership check with Set/Hash:
SISMEMBER target_cities "Ankara"   # is it in the set? (IN-like)
EXISTS customer:1                 # does the key exist?

В Redis нет подзапросов. Для проверки принадлежности значения используйте SISMEMBER на Set (аналог IN). Команда EXISTS — не подзапрос; она возвращает, существует ли заданный ключ. Реляционные проверки существования выполняются на стороне приложения чтением нескольких ключей.

Elasticsearch

# IN equivalent: terms query
GET customer/_search
{ "query": { "terms": { "city": ["Ankara", "Izmir"] } } }

# Field existence: exists query
GET customer/_search
{ "query": { "exists": { "field": "phone" } } }

В Elasticsearch эквивалент IN — запрос 'terms' (совпадение с одним из нескольких значений), а NOT IN — terms внутри bool must_not. Запрос 'exists' не соответствует SQL EXISTS; он проверяет, есть ли у поля значение в документе. Коррелированный EXISTS между таблицами обычно требует денормализации.

Открыть в DBAtlas →