Оператор exists
Операторы EXISTS и NOT EXISTS не сравнивают значения, а проверяют, вернул ли подзапрос хотя бы одну строку или нет.
Основные параметры такой проверки:
1) тест на наличие строк в результате выполнения подзапроса не сравнивает значения, поэтому не сопровождается оператором сравнения;
2) запрос может быть простым или сложным, но обычно он сложный;
3) запрос может считать любое количество столбцов и строк;
4) предложение SELECT в подзапросе задается как SELECT * для считывания всех столбцов. Нет необходимости указывать отдельные названия столбцов, так как EXISTS просто проверяет наличие строк, которые соответствуют условию запроса; сами значения в строках не рассматриваются;
5) если запрос считывает хотя бы одну строку, тест на EXISTS становится истинным, а тест на NOT EXISTS – ложным;
6) если запрос не считывает строки, тест на NOT EXISTS становится истинным, а тест на EXISTS – ложным;
7) строка запроса, содержащая во всех полях NULL, все равно считается строкой (тест на EXISTS становится истинным, а тест на NOT EXISTS – ложным).
Выполнение теста на наличие выборки
WHERE [NOT] EXISTS (subquery)
subquery – это запрос, который считывает любое количество столбцов и строк.
Если запрос считывает хотя бы одну строку, условие EXISTS задается как истинное. Если запрос считывает пустые строки, условие EXISTS задается как ложное.
Чтобы изменить результат тестирования, необходимо указать NOT.
Предложение HAVING имеет такую же структуру:
HAVING [NOT] EXISTS (subquery)
Примеры
1) Отобразить список всех издательств, которые опубликовали биографии
SELECT pub_name
FROM publishers p
WHERE EXISTS
(SELECT *
FROM titles t
WHERE t.pub_id = p.pub_id
AND type = 'biography');
2) Отобразить список авторов, которые не написали ни одной книги (в том числе в соавторстве).
SELECT au_id, au_fname, au_lname
FROM authors a
WHERE NOT EXISTS
(SELECT *
FROM title_authors ta
WHERE ta.au_id = a.au_id);
3) Отобразить список авторов, которые живут в городе, где расположено издательство
SELECT au_id, au_lname, au_fname, city
FROM authors a
WHERE EXISTS
(SELECT *
FROM publishers p
WHERE p.city = a.city);
Можно также использовать функцию COUNT(*), чтобы определить, считывает ли запрос хотя бы одну строку. COUNT(*), как правило, не так эффективна, как EXISTS. СУБД перестает обрабатывать запрос EXISTS тогда, когда определит, что он считывает хотя бы одну строку. В то же время COUNT(*) заставляет СУБД обработать весь запрос.
Запрос аналогичен запросу 3, но работает медленнее.
SELECT au_id, au_lname, au_fname, city
FROM authors a
WHERE
(SELECT COUNT(*)
FROM publishers p
WHERE p.city = a.city) > 0;
Операторы для работы с несколькими запросами
1.UNION
Иногда требуется как-то работать с несколькими результатами запросов.
Комбинировать строки можно с помощью операторов UNION, INTERSECT и EXCEPT. Данные операторы предназначены для преобразования двух команд SELECT в единый результат. Можно смешивать эти операторы так, чтобы комбинировать несколько таблиц, а не только две. Оператор UNION из перечисленных используется чаще всего.
Оператор UNION комбинирует результаты двух запросов в один результат, который объединяет строки, считанные двумя запросами. Выражение UNION удаляет из результата посторяющиеся строки, выражение UNION ALL сохраняет повторы. Операторы UNION просты, но имеют ряд ограничений:
1) списки столбцов команды SELECT в двух запросах должны включать одинаковое число столбцов;
2) соответствующие столбцы в двух запросах должны быть заданы в одинаковом порядке;
3) если имена соответствующих столбцов совпадает, то их название будет использовано в результате. Если названия соответствующих столбцов различаются, то СУБД самостоятельно определит имя столбца в результате. Большинство СУБД заимствуют названия столбцов для результата из первого отдельного запроса в команде UNION
4) предложения GROUP BY и HAVING можно задавать только в отдельных запросах, их нельзя использовать для изменения конечного результата
5) предложение ORDER BY может использоваться только в последнем запросе команды UNION. Эта сортировка применяется к конечному результату после комбинирования. Так как названия столбцов в итоге различаются для разных СУБД, для указания порядка сортировки проще всего использовать относительные положения столбцов. То есть вместо названия столбца можно указывать порядковый номер этого столбца.
Комбинирование строк
select_statement1
UNION [ALL]
select_statement2
select_statement1 и select_statement2 - команды SELECT. Необходимо задать количество и порядок столбцов для двух команд, а тип данных в соответствующих столбцах должен быть совместимым. Если опция ALL не задается, то повторяющиеся строки будут удалены из результата.
Примеры:
1) отобразить список городов, в которых живут авторы и находятся издательства.
SELECT city FROM authors
UNION
SELECT city FROM publishers
По умолчанию UNION удаляет из результата повторяющиеся строки.
2) Отобразить список фамилий авторов и названий издательств из города Москва, указав тип, то есть нужно указать, автор это или издательство. Отсортировать по названию/имени.
SELECT 'author' AS "Type",
lname,
city
FROM authors
WHERE city='Moscow'
UNION
SELECT 'publisher',
name,
city
FROM publishers
WHERE city='Moscow'
ORDER BY 1 ASC
UNION является коммутативной командой: A UNION B - это то же самое, что B UNION A.
Для объединения в одной команде UNION и UNION ALL следует пользоваться круглыми скобками, чтобы задать порядок расчетов.
SELECT * FROM table1
UNION ALL
(
SELECT * FROM table2
UNION
SELECT * FROM table3
)
и
(
SELECT * FROM table1
UNION ALL
SELECT * FROM table2
)
UNION
SELECT * FROM table3
Первая команда удаляет повторы в объединении table2 и table3, но не удаляет их в результате и table1. Вторая команда сохраняет повторы в объединении table1 и table2, но удаляет их в последующем объединении с table3, поэтому ALL не влияет на конечный результат команды.
2. INTERSECT
Команда INTERSECT преобразует результаты двух запросов в один результат, который включает все общие строки в результатах выполнения двух запросов. Пересечения имеют такие же ограничения, как и объединения UNION.
Поиск общих строк
select_statement1
INTERSECT
select_statement2
select_statement1 и select_statement2 - команды SELECT. Необходимо задать количество и порядок столбцов для двух команд, а тип данных в соответствующих столбцах должен быть совместимым. Повторяющиеся строки будут удалены из результата.
INTERSECT является коммутативной командой: A INTERSECT B – это то же самое, что B INTERSECT A.
В SQL у INTERSECT более высокий приоритет по сравнению с UNION и EXCEPT, но в конкретной СУБД приоритеты могут быть другими. Чтобы задать порядок расчета в запросах со смешанными операторами, стоит пользоваться круглыми скобками.
Очень полезно воспринимать UNION как логический вариант OR, а INTERSECT - как логический вариант AND. Например, если нужно узнать, какие товары поставляются продавцов A и B.
SELECT product_id
FROM vendor_a_product_list
UNION
SELECT product_id
FROM vendor_b_product_list
Если нужно узнать, какие товары поставляются и продавцом A, и продавцом B, то:
SELECT product_id
FROM vendor_a_product_list
INTERSECT
SELECT product_id
FROM vendor_b_product_list
Microsoft Access, Microsoft SQL Server и MySQL не поддерживают команду INTERSECT. Её можно переписать с использованием INNER JOIN или с использованием предложения EXISTS.
SELECT DISTINCT authors.city
FROM authors
INNER JOIN publishers
ON authors.city = publishers.city
SELECT DISTINCT city
FROM authors
WHERE EXISTS
(SELECT *
FROM publishers
WHERE authors.city = publishers.city)
3. EXCEPT
Команда EXCEPT преобразует результаты двух запросов в один результат, который включает только строки первого запроса. Разница между EXCEPT и INTERSECT в том, что A INTERSECT B включает строки из таблицы A, которые повторяются в таблице B, а A EXCEPT B включает строки из таблицы A, которые не повторяются в таблице B.
Поиск разных строк
select_statement1
EXCEPT
select_statement2
select_statement1 и select_statement2 - команды SELECT. Необходимо задать количество и порядок столбцов для двух команд, а тип данных в соответствующих столбцах должен быть совместимым. Повторяющиеся строки будут удалены из результата.
В отличие от команд UNION и INTERSECT, команда EXCEPT не является коммутативной:
A EXCEPT B – это не то же самое, что B EXCEPT A.
Отобразить список городов, в которых живут авторы, но нет издательств.
SELECT city FROM authors
EXCEPT
SELECT city FROM publishers
Microsoft Access, Microsoft SQL Server и MySQL не поддерживают команду INTERSECT.
Её можно переписать с использованием внешнего запроса или с использованием предложения NOT EXISTS или NOT IN.
SELECT DISTINCT a.city
FROM authors a
LEFT OUTER JOIN publishers p
ON a.city = p.city
WHERE p.city IS NULL
или
SELECT DISTINCT city
FROM authors
WHERE NOT EXISTS
(SELECT *
FROM publishers
WHERE authors.city = publishers.city)
или
SELECT DISTINCT city
FROM authors
WHERE NOT IN
(SELECT city
FROM publishers)
