EXISTS в Oracle SQL — это логический оператор, который проверяет наличие хотя бы одной строки в подзапросе. Он особенно эффективен в фильтрации по связанным таблицам. Эта статья покажет, как использовать EXISTS и NOT EXISTS с подзапросами, условиями и фильтрацией. Примеры — от простых до вложенных.
🖋️ Синтаксис оператора EXISTS
SELECT * FROM таблица
WHERE EXISTS (
SELECT 1 FROM другая_таблица WHERE условие
);
Также используется:
NOT EXISTS— возвращает TRUE, если подзапрос не возвращает ни одной строки
🔄 Где используется EXISTS
- Проверка наличия связанных записей
- Вложенные фильтрации
- Условия с динамическими подзапросами
- Работа с коррелированными запросами
📊 Примеры использования EXISTS в Oracle SQL
- EXISTS с простой связью:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM departments d WHERE d.department_id = e.department_id
);
- NOT EXISTS для отсутствия связи:
SELECT * FROM departments d
WHERE NOT EXISTS (
SELECT 1 FROM employees e WHERE e.department_id = d.department_id
);
- EXISTS с подзапросом по фильтру:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM job_history j WHERE j.employee_id = e.employee_id AND j.department_id = 80
);
- EXISTS внутри SELECT:
SELECT first_name,
CASE WHEN EXISTS (
SELECT 1 FROM job_history j WHERE j.employee_id = e.employee_id
) THEN 'Есть история' ELSE 'Нет истории' END AS история
FROM employees e;
- EXISTS в DELETE:
DELETE FROM employees e
WHERE EXISTS (
SELECT 1 FROM job_history j WHERE j.employee_id = e.employee_id
);
- EXISTS с фильтрацией зарплаты:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM employees e2 WHERE e2.salary > 10000 AND e2.employee_id = e.employee_id
);
- EXISTS с подзапросом на подчинённых:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM employees sub WHERE sub.manager_id = e.employee_id
);
- EXISTS с датами:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM job_history j WHERE j.employee_id = e.employee_id AND j.start_date < TO_DATE('2022-01-01', 'YYYY-MM-DD')
);
- EXISTS с JOIN внутри подзапроса:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM departments d JOIN locations l ON d.location_id = l.location_id
WHERE d.department_id = e.department_id AND l.city = 'Seattle'
);
- EXISTS в UPDATE:
UPDATE employees e SET salary = salary * 1.05
WHERE EXISTS (
SELECT 1 FROM job_history j WHERE j.employee_id = e.employee_id AND j.department_id = 80
);
- EXISTS с вложенными подзапросами:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM departments d
WHERE d.department_id = (
SELECT department_id FROM job_history j WHERE j.employee_id = e.employee_id AND ROWNUM = 1
)
);
- EXISTS с аналитикой:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM (
SELECT employee_id, RANK() OVER (ORDER BY salary DESC) AS rnk FROM employees
) ranked WHERE ranked.employee_id = e.employee_id AND rnk <= 5
);
- EXISTS и подстановка строки:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM departments d WHERE d.department_name LIKE 'Sal%' AND d.department_id = e.department_id
);
- EXISTS с JSON_VALUE:
SELECT * FROM employees_json ej
WHERE EXISTS (
SELECT 1 FROM dual WHERE JSON_VALUE(ej.data, '$.status') = 'active'
);
- EXISTS с фильтрацией по NULL:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM job_history j WHERE j.employee_id = e.employee_id AND j.end_date IS NULL
);
- EXISTS с коррелированным подзапросом по дате:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM job_history j WHERE j.employee_id = e.employee_id AND j.start_date > e.hire_date
);
- EXISTS в SELECT внутри SELECT:
SELECT employee_id,
CASE WHEN EXISTS (
SELECT 1 FROM job_history j WHERE j.employee_id = e.employee_id
) THEN 'Работал раньше' ELSE 'Нет данных' END AS статус
FROM employees e;
- EXISTS в фильтрации агрегатов:
SELECT department_id FROM departments d
WHERE EXISTS (
SELECT 1 FROM employees e WHERE e.department_id = d.department_id
GROUP BY e.department_id HAVING COUNT(*) > 5
);
- EXISTS с ROWNUM:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM job_history j WHERE j.employee_id = e.employee_id AND ROWNUM = 1
);
- EXISTS с функцией UPPER:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM departments d WHERE UPPER(d.department_name) = 'IT' AND d.department_id = e.department_id
);
- EXISTS с подзапросом на DISTINCT:
SELECT * FROM employees e
WHERE EXISTS (
SELECT DISTINCT department_id FROM job_history j WHERE j.employee_id = e.employee_id
);
- EXISTS с подзапросом WHERE IN:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM departments d WHERE d.department_id IN (10, 20, 30) AND d.department_id = e.department_id
);
- EXISTS с JSON_TABLE:
SELECT * FROM JSON_TABLE(
'{"employees": [{"id":1,"status":"active"},{"id":2,"status":"inactive"}]}'
, '$.employees[*]'
COLUMNS (id NUMBER PATH '$.id', status VARCHAR2(20) PATH '$.status')
) WHERE EXISTS (
SELECT 1 FROM dual WHERE status = 'active'
);
- EXISTS с VIEW:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM v_active_employees v WHERE v.employee_id = e.employee_id
);
- EXISTS в PL/SQL:
DECLARE
v_exists BOOLEAN;
BEGIN
SELECT CASE WHEN EXISTS (
SELECT 1 FROM employees WHERE department_id = 50
) THEN TRUE ELSE FALSE END INTO v_exists FROM dual;
END;
- EXISTS с подзапросом IN SELECT:
SELECT employee_id FROM employees e
WHERE EXISTS (
SELECT 1 FROM departments d WHERE d.department_id IN (
SELECT department_id FROM job_history WHERE employee_id = e.employee_id
)
);
- EXISTS с аналитикой RANK:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM (
SELECT employee_id, RANK() OVER (ORDER BY salary DESC) AS rnk FROM employees
) ranked WHERE ranked.rnk <= 3 AND ranked.employee_id = e.employee_id
);
- EXISTS с CASE в подзапросе:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM job_history j
WHERE j.employee_id = e.employee_id
AND CASE WHEN j.department_id = 60 THEN 1 ELSE 0 END = 1
);
- EXISTS с логикой AND и OR:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM departments d
WHERE d.department_id = e.department_id AND (d.location_id = 1700 OR d.location_id = 1800)
);
- EXISTS с TO_CHAR:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM job_history j WHERE j.employee_id = e.employee_id
AND TO_CHAR(j.start_date, 'YYYY') = '2022'
);
- EXISTS с INITCAP:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM departments d WHERE INITCAP(d.department_name) = 'Finance'
AND d.department_id = e.department_id
);
- EXISTS в аналитике MEDIAN:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM (
SELECT employee_id, MEDIAN(salary) OVER () AS med FROM employees
) m WHERE m.salary >= med AND m.employee_id = e.employee_id
);
- EXISTS с аналитикой PERCENT_RANK:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM (
SELECT employee_id, PERCENT_RANK() OVER (ORDER BY salary) AS p FROM employees
) x WHERE p < 0.5 AND x.employee_id = e.employee_id
);
- EXISTS с аналитикой NTILE:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM (
SELECT employee_id, NTILE(4) OVER (ORDER BY hire_date) AS tile FROM employees
) x WHERE tile = 1 AND x.employee_id = e.employee_id
);
- EXISTS с внешней таблицей:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM external_employees ex WHERE ex.employee_id = e.employee_id
);
- EXISTS с EXTRACT:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM job_history j WHERE EXTRACT(YEAR FROM j.start_date) = 2021 AND j.employee_id = e.employee_id
);
- EXISTS с NVL:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM job_history j WHERE NVL(j.department_id, 0) = 90 AND j.employee_id = e.employee_id
);
- EXISTS и LENGTH:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM departments d WHERE LENGTH(d.department_name) > 5 AND d.department_id = e.department_id
);
- EXISTS и TRIM:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM departments d WHERE TRIM(d.department_name) = 'Finance'
AND d.department_id = e.department_id
);
- EXISTS с GROUPING SETS:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM (
SELECT department_id FROM employees
GROUP BY GROUPING SETS ((department_id))
) g WHERE g.department_id = e.department_id
);
- EXISTS с MERGE:
MERGE INTO employees e USING dual d
ON (EXISTS (
SELECT 1 FROM job_history j WHERE j.employee_id = e.employee_id
))
WHEN MATCHED THEN UPDATE SET salary = salary + 100;
- EXISTS и RATIO_TO_REPORT:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM (
SELECT employee_id, RATIO_TO_REPORT(salary) OVER () AS r FROM employees
) x WHERE r > 0.05 AND x.employee_id = e.employee_id
);
- EXISTS и NULLIF:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM job_history j WHERE NULLIF(j.department_id, 0) = 80 AND j.employee_id = e.employee_id
);
- EXISTS и DUMP:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM departments d WHERE DUMP(d.department_name) LIKE '%70%' AND d.department_id = e.department_id
);
- EXISTS и CASE в подзапросе:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM job_history j WHERE j.employee_id = e.employee_id
AND CASE WHEN j.department_id = 100 THEN 1 ELSE 0 END = 1
);
- EXISTS с INLINE VIEW:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM (
SELECT department_id FROM departments WHERE location_id = 1700
) sub WHERE sub.department_id = e.department_id
);
- EXISTS с UNPIVOT:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM (
SELECT * FROM departments
UNPIVOT (val FOR col IN (location_id, manager_id))
) u WHERE u.val = e.department_id
);
- EXISTS с двойным EXISTS:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM departments d WHERE d.department_id = e.department_id AND
EXISTS (
SELECT 1 FROM locations l WHERE l.location_id = d.location_id AND l.country_id = 'US'
)
);
- EXISTS с UNION ALL:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM (
SELECT department_id FROM job_history WHERE department_id = 80
UNION ALL
SELECT department_id FROM departments WHERE location_id = 1700
) combined WHERE combined.department_id = e.department_id
);
- EXISTS с аналитикой и фильтрацией:
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM (
SELECT employee_id, RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS rnk
FROM employees
) ranked WHERE ranked.employee_id = e.employee_id AND rnk <= 3
);
📆 Заключение: зачем изучать EXISTS в Oracle SQL
Оператор EXISTS — мощный способ фильтрации, когда нужно проверить наличие данных по условию. Особенно полезен в коррелированных запросах, когда строки основной таблицы фильтруются по связанным таблицам.
Зная EXISTS, ты сможешь эффективно контролировать наличие данных, строить вложенные фильтры и создавать более читаемые и оптимальные запросы.