EXISTS: примеры (SQL)

Использование EXISTS в SQL Server: полное руководство
Раздел: Предикаты, Условия
EXISTS(EXISTS subquery): Boolean

Описание функции EXISTS

Функция EXISTS в MS SQL Server является предикатом, который используется для проверки существования строк в результатах подзапроса. Она не является функцией в классическом понимании, а представляет собой логический оператор, который возвращает TRUE или FALSE.

Основное применение EXISTS - выполнение проверок в конструкциях WHERE, HAVING или IF для определения, возвращает ли подзапрос хотя бы одну запись.

Синтаксис: EXISTS (subquery)

Параметры:

  • subquery - ограниченный подзапрос. Может быть любым корректным SELECT-запросом, но не может включать предложения FOR BROWSE, INTO, COMPUTE, ORDER BY (без TOP) или XML.

Возвращаемое значение:

  • TRUE, если подзапрос возвращает хотя бы одну строку
  • FALSE, если подзапрос не возвращает ни одной строки
  • NULL, если подзапрос возвращает NULL (редкий случай при работе с NULL-значениями)

Особенности работы:

  • Подзапрос выполняется до тех пор, пока не будет найдена первая совпадающая запись
  • Производительность EXISTS обычно выше, чем использование COUNT(*), так как выполнение прекращается после нахождения первой строки
  • В подзапросе обычно используется корреляция с внешним запросом через WHERE

Базовые примеры использования

Пример 1: Проверка существования записей

SELECT EmployeeID, FirstName, LastName
FROM Employees e
WHERE EXISTS (
    SELECT 1 
    FROM Orders o 
    WHERE o.EmployeeID = e.EmployeeID
    AND o.OrderDate > '2023-01-01'
);

Результат: Будут возвращены все сотрудники, которые оформили заказы после 1 января 2023 года

EmployeeID | FirstName | LastName
-----------|-----------|---------
1          | John      | Doe
3          | Jane      | Smith
5          | Robert    | Johnson

Пример 2: EXISTS с NOT

SELECT ProductID, ProductName
FROM Products p
WHERE NOT EXISTS (
    SELECT 1 
    FROM OrderDetails od 
    WHERE od.ProductID = p.ProductID
);

Результат: Будут возвращены товары, которые ни разу не заказывались

ProductID | ProductName
----------|------------
15        | Unordered Product A
27        | Unordered Product B

Похожие функции в MS SQL

В MS SQL Server существуют альтернативные способы проверки существования данных:

IN оператор: Проверяет, соответствует ли значение какому-либо значению в списке или результату подзапроса. Отличается тем, что сравнивает конкретные значения, а не просто проверяет наличие записей.

ANY/SOME операторы: Работают аналогично IN, но с возможностью использования операторов сравнения. ANY возвращает TRUE, если условие истинно для хотя бы одного значения в наборе.

COUNT агрегатная функция: Может использоваться для подсчета записей, но менее эффективна, чем EXISTS, так как требует полного сканирования.

Рекомендации по выбору:

  • EXISTS предпочтительнее для коррелированных подзапросов и проверок существования
  • IN эффективнее для сравнения с фиксированным списком значений
  • COUNT() стоит использовать, когда необходимо знать точное количество совпадений

Аналоги в других СУБД

PostgreSQL: EXISTS работает аналогично MS SQL. Пример:

SELECT id, name FROM users u 
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id);

Oracle: Синтаксис EXISTS идентичен. Особенность - часто используется с ROWNUM для ограничения результатов:

SELECT employee_id FROM employees e
WHERE EXISTS (SELECT 1 FROM departments d WHERE d.manager_id = e.employee_id AND ROWNUM = 1);

MySQL: Поддерживает EXISTS с версии 4.1. Работает аналогично, но есть особенности с оптимизацией подзапросов:

SELECT * FROM products p
WHERE EXISTS (SELECT * FROM order_items oi WHERE oi.product_id = p.id);

SQLite: Поддерживает EXISTS с версии 3.8.3. Синтаксис совместим с другими СУБД:

SELECT * FROM customers c
WHERE EXISTS (SELECT 1 FROM invoices i WHERE i.customer_id = c.id);

Основные отличия в оптимизации запросов и поддержке расширенных функций в сочетании с EXISTS.

Типичные ошибки

Ошибка 1: Использование SELECT * вместо SELECT 1 или конкретного поля

-- Неоптимально
WHERE EXISTS (SELECT * FROM table WHERE condition)

-- Оптимально
WHERE EXISTS (SELECT 1 FROM table WHERE condition)

Ошибка 2: Неправильная корреляция подзапроса

-- Ошибка: нет связи с внешним запросом
SELECT * FROM Employees e
WHERE EXISTS (SELECT 1 FROM Orders);

-- Правильно
SELECT * FROM Employees e
WHERE EXISTS (SELECT 1 FROM Orders o WHERE o.EmployeeID = e.EmployeeID);

Ошибка 3: Использование ORDER BY в подзапросе без TOP

-- Вызовет ошибку
WHERE EXISTS (
    SELECT 1 
    FROM Orders 
    WHERE EmployeeID = e.EmployeeID 
    ORDER BY OrderDate DESC
);

-- Корректно
WHERE EXISTS (
    SELECT TOP 1 1 
    FROM Orders 
    WHERE EmployeeID = e.EmployeeID 
    ORDER BY OrderDate DESC
);

Изменения в последних версиях

В MS SQL Server 2016 и более поздних версиях были улучшены алгоритмы оптимизации запросов с EXISTS:

  • Улучшена обработка коррелированных подзапросов в плане выполнения
  • Добавлены новые стратегии объединения запросов
  • Улучшена оценка кардинальности для запросов с EXISTS

В SQL Server 2019 появилась функция Scalar UDF Inlining, которая может оптимизировать пользовательские функции, используемые в условиях EXISTS.

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

Расширенные примеры

Пример 1: EXISTS с обновлением данных

Пример sql
UPDATE Products
SET Discontinued = 1
WHERE EXISTS (
    SELECT 1 
    FROM OrderDetails od 
    JOIN Orders o ON od.OrderID = o.OrderID
    WHERE od.ProductID = Products.ProductID
    AND o.OrderDate < DATEADD(YEAR, -5, GETDATE())
);

Результат: Товары, которые не заказывались более 5 лет, помечаются как снятые с производства

Пример 2: Вложенные EXISTS

Пример sql
SELECT c.CustomerID, c.CompanyName
FROM Customers c
WHERE EXISTS (
    SELECT 1
    FROM Orders o
    WHERE o.CustomerID = c.CustomerID
    AND EXISTS (
        SELECT 1
        FROM OrderDetails od
        WHERE od.OrderID = o.OrderID
        AND od.UnitPrice > 1000
    )
);

Результат: Клиенты, которые делали заказы с товарами дороже 1000 у.е.

Пример 3: EXISTS с оконными функциями

Пример sql
SELECT e.EmployeeID, e.LastName,
       CASE 
           WHEN EXISTS (
               SELECT 1 
               FROM (
                   SELECT EmployeeID, 
                          ROW_NUMBER() OVER (ORDER BY OrderDate DESC) as rn
                   FROM Orders
                   WHERE EmployeeID = e.EmployeeID
               ) t 
               WHERE t.rn <= 3
           ) THEN 'Active'
           ELSE 'Inactive'
       END as Status
FROM Employees e;

Пример 4: EXISTS с табличной переменной

Пример sql
DECLARE @TargetProducts TABLE (ProductID INT);
INSERT INTO @TargetProducts VALUES (10), (20), (30);

SELECT p.ProductID, p.ProductName
FROM Products p
WHERE EXISTS (
    SELECT 1 
    FROM @TargetProducts tp 
    WHERE tp.ProductID = p.ProductID
);

MS SQL EXISTS function comments

En
EXISTS Specifies a subquery to test for the existence of rows