Язык SQL — Использование подзапросов в команде SELECT

Для создания сложных запросов можно использовать вложенные запросы (подзапросы).

Подзапросом называется команда SELECT, которая находится внутри другой команды языка манипулирования данными.

Именно возможность вложения команд SQL друг в друга является причиной, по которой SQL получил свое название — Structured Query Language (структурированный язык запросов). Каждая включающая команда — следующий по старшинству уровень в подзапросе — представляет собой внешний уровень для внутреннего подзапроса.

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

Подзапросы можно разделить на следующие группы в зависимости от возвращаемых результатов:

  • Скалярные – это подзапросы, возвращающие единственное значение.
  • Векторные – подзапросы, возвращающие от 0 до нескольких однотипных элементов (список элементов).
  • Табличные – это подзапросы, возвращающие таблицу.

Сначала рассмотрим использование подзапросов в команде SELECT. Подзапросы могут располагаться в разных частях этой команды:

  • в предложении SELECT – скалярные;
  • в предложении FROM – табличные некоррелированные; в предложении WHERE – любые;
  • в предложении HAVING – любые.

Достаточно упрощенный синтаксис подзапроса, иллюстрирующий вложение подзапроса в предложении WHERE команды SELECT, выглядит следующим образом:

SELECT [DISTINCT] <список_выбора_запроса>

FROM <список_таблиц>

WHERE <выражение> [NОТ] IN / оператор_сравнения [ANY / ALL] / [NOT] EXISTS

( SELECT [DISTINCT] <список_выбора_подзапроса>

FROM <список_таблиц >

WHERE <условия>)

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

Как же работают подзапросы? Очень просто — они возвращают результаты внутреннего запроса во внешнюю команду и имеют две основные формы: некоррелированную (noncorrelated) и коррелированную (correlated).

В чем различие этих форм? Коррелированные подзапросы содержат ссылки на значения полей в запросе верхнего уровня, а некоррелированные – не содержат. Поэтому некоррелированный подзапрос выполняется автономно от внешней команды. После чего внешний запрос выполняет то или иное действие, основываясь на полученных результатах выполнения внутреннего запроса. Таким образом, некоррелированный подзапрос вычисляется один раз для запроса верхнего уровня.

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

Коррелированная форма подзапроса представляет собой следующее действие — внешняя команда SQL предоставляет значения для внутреннего подзапроса, которые будут использоваться при его выполнении. После этого результаты выполнения подзапроса возвращаются во внешний запрос. В процессе выполнения коррелированный подзапрос вычисляется для каждой строки запроса верхнего уровня.

1. Скалярные подзапросы

Скалярные подзапросы возвращают единственное значение. Они начинаются с простого (немодифицированного) оператора сравнения =, <>, >, >=, <, или <=.

Общая форма таких запросов имеет вид:

Начало команды SELECT, INSERT, UPDATE, DELETE или подзапроса WHERE <выражение> <оператор _сравнения> (подзапрос) [Окончание команды SELECT, INSERT, UPDATE, DELETE или подзапроса]

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

По этой причине в подзапросы с оператором сравнения часто включают агрегатные функции, поскольку они гарантированно возвращают единственное значение.

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

SELECT Наименование, [Отклонение от средней цены] = Цена- (SELECT AVG(Цена) FROM Склад)

FROM Склад

WHERE Цена > (SELECT AVG(Цена) FROM Склад)

Результат выполнения запроса приведен на рис. 2

Запрос с некоррелированным скалярным подзапросом

Рис. 2. Запрос с некоррелированным скалярным подзапросом

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

С операторами сравнения можно использовать и коррелированные подзапросы.

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

SELECT pl.Тип

FROM Приход p1

GROUP BY pl.Тип

HAVING MAX(pl. Цена) >

(SELECT AVG1.5 * p2.Цена)

FROM Приход p2

WHERE pl.Тип = p2.Тип)

Результат выполнения запроса приведен на рис. 3

Запрос с коррелированным скалярным подзапросом

Рис. 3. Запрос с коррелированным скалярным подзапросом

Обратите внимание, что коррелированный подзапрос входит в предложение HAVING внешнего запроса. Следует добавить, что подзапрос в этом случае выполняется по одному разу для каждой группы, определенной во внешнем запросе, т. е. по одному разу для каждого типа товара.

2. Векторные подзапросы

Векторные подзапросы либо не возвращают ни одного значения, либо возвращают несколько значений. Такие подзапросы начинаются с операторов IN, NOT IN или оператора сравнения с ключевыми словами ANY или ALL.

2.1. Подзапросы, начинающиеся с оператора IN

Такие подзапросы имеют следующую общую форму:

Начало команды SELECT, INSERT, UPDATE, DELETE или подзапроса

WHERE <выражение> [NOT] IN (подзапрос)

[Окончание команды SELECT, INSERT, UPDATE, DELETE или подзапроса]

Результатом выполнения такого внутреннего подзапроса является список, не включающий ни одного или включающий несколько значений. После того как он возвратит результаты, к их обработке приступит внешний запрос.

Оператор IN используется для отбора во внешнем запросе только тех записей, которые содержат значения, совпадающие с одним из отобранных внутренним запросом.

Рассмотрим примеры, с помощью которых постараемся разобраться, как выполняются коррелированные и некоррелированные запросы.

Пример: необходимо узнать поставщиков, которые поставляли товар 25.11.2020

Вариант 1. Запрос с некоррелированным подзапросом:

SELECT Название

FROM Поставщик

WHERE Код_постав IN (SELECT Код_постав FROM Документ WHERE Дата = ’25.11.2020’)

Результат выполнения запроса приведен на рис. 4

Запрос с некоррелированным подзапросом и оператором IN

Рис. 4. Запрос с некоррелированным подзапросом и оператором IN

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

SELECT Код_постав

FROM Документ

WHERE Дата = '25.11.2020'

возвращает коды тех поставщиков, которые поставляли товар 25.11.2020 (например: 120 и 220).

Затем эти значения передаются во внешний запрос, который ищет в таблице Поставщик названия поставщиков, соответствующие найденным кодам:

SELECT Название

FROM Поставщик

WHERE Код_постав IN (120, 220)

Например, результатом этого запроса будет:

Веденеев Д.В.

Гусь П.А.

Вариант 2. Запрос с коррелированным подзапросом, позволяющий узнать поставщиков, которые поставляли товар 15.03.2020

SELECT Название

FROM Поставщик A

WHERE '25.11.2020' IN (SELECT Дата FROM Документ WHERE A.Код_постав = Код_постав)

В коррелированном запросе внутренний подзапрос не может быть реализован отдельно: он для своего выполнения должен получить данные из внешнего запроса и выполняется последовательно для каждой строки во внешнем запросе. Так для рассматриваемого примера внутренний запрос будет выполняться столько раз, сколько строк содержит внешняя таблица Поставщик.

Как же выполняется обработка в этом случае? Внешний запрос отыскивает первое имя в таблице Поставщик. В нашей учебной базе это Петров А.Н., имеющий код равный 100. Теперь внутренний запрос выполняет операцию соединения, чтобы найти требуемые строки в таблице Документ. Предположим, что таких строк обнаружено две, в которых значение поля Дата равно соответственно 13.01.2020 и 20.07.2020.

После этого, внутренний запрос возвращает результат во внешний запрос, где полученные значения Документ.Дата сравниваются с константой ‘25.11.2020’. Следовательно, Петров А.Н. в результат запроса не попадет.

Затем снова начинает работать подзапрос, но на этот раз с кодом поставщика второй строки из таблицы Поставщик. Он находит одну строку с датой 20.10.2020 отличной от 25.11.2020. Соответственно внешний запрос не найдет поставщика, удовлетворяющему критерию, и подзапрос начинает выполняться в третий раз.

Для третьей строки таблицы Поставщик cо значением Код_постав, равным 120 (поставщик Веденеев Д.В.), подзапрос находит две строки. Одна из них относится к дате ‘25.11.2020’, но этого достаточно — этот поставщик попадает в результирующий список. После этого запрос продолжится выполняться для каждой строки внешнего запроса.

Подобным образом будут обработаны все записи таблицы Поставщик.

Несмотря на всю разницу выполнения некоррелированного и коррелированного запросов, результаты выполнения обоих запросов будут выглядеть совершенно одинаково (рис. 4).

Таким образом, порядок выполнения команды SELECT с коррелированным подзапросом выглядит следующим образом:

  1. Выбирается строка из таблицы, имя которой указано во внешнем запросе.
  2. Выполняется подзапрос и полученное значение применяется для анализа этой строки в условии предложения WHERE внешнего запроса.
  3. По результату оценки этого условия принимается решение о включении или не включении строки в состав выходных данных.
  4. Процедура повторяется для следующей строки таблицы внешнего запроса.

Вариант 3. Этот же запрос можно сформулировать как запрос на соединение таблиц.

SELECT DISTINCT Название

FROM Поставщик p, Документ d

WHERE p.Код_постав = d. Код_постав AND Дата = '25.11.2020'

Ключевое слово DISTINCT добавлено в список выбора, чтобы названия каждого поставщика отображались только один раз. Результат выполнения этого запроса будет таким же, как и в вариантах 1 и 2. Оператор NOT IN используется для отбора во внешнем запросе только тех записей, которые содержат значения, не совпадающие ни с одним из отобранных внутренним запросом.

Например, запрос

SELECT Название

FROM Поставщик

WHERE Код_постав NOT IN (SELECT Код_постав

FROM Документ

WHERE Дата = '25.11.2020')

отберет только тех поставщиков, дата поставки которых не совпадает с датой 25.11.2020.

2.2. Подзапросы, включающие ключевые слова ANY или ALL

Еще один вид подзапросов, которые либо не возвращают ни одного значения, либо возвращают несколько значений, использует оператор сравнения, модифицированный ключевыми словами ANY или ALL. Все они имеют следующую общую форму:

Начало команды SELECT, INSERT, UPDATE, DELETE или подзапроса

WHERE <выражение> <оператор_сравнения> [ANY / ALL] (подзапрос)

[Окончание команды SELECT, INSERT, UPDATE, DELETE или подзапроса]

Рассмотрим, для чего нужны ключевые слова ANY и ALL.

Проверка ANY

В проверке ANY, для того чтобы сравнить проверяемое значение со столбцом данных, отобранным внутренним запросом, используется один из шести операторов сравнения (=, <>, <, <=, >, >=). Проверяемое значение поочередно сравнивается с каждым элементом, содержащимся в столбце.

В стандарте ANSI/ISO для языка SQL содержатся подробные правила, определяющие результаты проверки ANY, когда проверяемое значение сравнивается со столбцом результатов внутреннего запроса:

  • Если операция сравнения имеет значение TRUE хотя бы для одного значения в столбце, то проверка ANY возвращает значение TRUE (имеется некоторое значение, полученное внутренним запросом, для которого условие сравнения выполняется).
  • Если операция сравнения имеет значение FALSE для всех значений в столбце, то проверка ANY возвращает значение FALSE (можно утверждать, что ни для одного значения, возвращенного внутренним запросом, условие сравнения не выполняется).
  • Если внутренний запрос возвращает результат в виде пустого столбца, то проверка ANY возвращает значение FALSE (в результате выполнения внутреннего запроса не получено ни одного значения, для которого выполнялось бы условие сравнения).
  • Если операция сравнения не имеет значение TRUE ни для одного значения в столбце, но в нем присутствуют одно или несколько значений NULL, то проверка ANY возвращает результат NULL. В этой ситуации невозможно с определенностью утверждать, существует ли полученное внутренним запросом значение, для которого выполняется условие сравнения.

Например: выбрать принтеры, цена которых больше цены любого монитора

SELECT Наименование, Приход.Цена

FROM Приход, Склад

WHERE Приход.Ном_ном = Склад. Ном_ном AND Тип = 'Принтер'AND Цена > ANY(SELECT Цена FROM Приход WHERE Тип = ‘Монитор’)

Внутренний запрос находит все мониторы и возвращает столбец, содержащий цену этих мониторов. Внешний запрос по очереди проверяет все строки таблицы Приход. Предложение WHERE внешнего запроса сравнивает цену принтера с ценой каждого монитора, отобранного внутренним запросом.

Если есть хотя бы один монитор, который стоит меньше, чем заданное проверяемое значение, то проверка > ANY возвращает значение TRUE, а наименование принтера заносится в таблицу результатов запроса. Если таких принтеров нет, название принтера в таблицу результатов запроса не попадает. В учебной базе данных содержаться мониторы с ценами 9820,00 руб., 5820,00 руб., 4790,00 руб., 16790,00 руб. и принтеры с ценами 6520,00 руб., 3990,00 руб., 5200,00 руб., 6990,00 руб. Следовательно, принтер с ценой 3990,00 руб. в итоговую таблицу не попадет.

Результат выполнения запроса приведен на рис. 5.

Запрос с некоррелированным подзапросом и проверкой >ANY

Рис. 5. Запрос с некоррелированным подзапросом и проверкой >ANY

Надо отметить, что оператор ANY не полностью однозначен.

Создадим запрос, чтобы найти все поступления на склад, сумма которых меньше любой суммы поступления до 14.11.2020

SELECT *, Цена * Количество AS 'Сумма'

FROM Приход

WHERE Цена * Количество < ANY (SELECT Цена * Количество FROM Приход A, Документ B WHERE A.Ном_док = B.Ном_док AND Дата < '14.11.2020')

Внутренний запрос SELECT Цена * Количество

FROM Приход A, Документ B

WHERE A.Ном_док = B.Ном_док

AND Дата < '14.11.2020'

выводит следующие значения 65200, 32600, 39900 и 41600. Спрашивается: с какой суммой попадут товары в результирующий запрос?

Результат выполнения запроса приведен на рис. 6.

Как следует из рис. 6, в результирующий запрос попали все товары с суммой меньшей 65200.

Запрос с некоррелированным подзапросом и проверкой <ANY

Рис. 6. Запрос с некоррелированным подзапросом и проверкой <ANY

Для модификации операторов сравнения используются следующие операторы:

< ANY – означает: меньше, чем любое полученное число; эквивалентно < для самого большего полученного числа. Поэтому < ANY (1,2,3) означает меньше 3.

< = ANY – означает: меньше или равно любому полученному числу; эквивалентно операции < = для самого большего полученного числа.

> ANY – означает: больше, чем наименьшее выбранное значение. Поэтому > ANY (1,2,3) означает больше 1.

> = ANY – означает: больше или равно для самого меньшего полученного числа.

= ANY – полностью эквивалентен оператору IN.

<>ANY не эквивалентно NOT IN и существенно отличается от оператора NOT IN:

  • <>ANY (a, b, c) — <> а ИЛИ <> b ИЛИ <> с
  • NOT IN (a,b,c) — <> а И <> b И <> с

Проверка ALL

В проверке ALL используется один из шести операторов (=, <>, <, <=, >, >=) для сравнения проверяемого значения со столбцом данных, отобранным внутренним запросом. Проверяемое значение поочередно сравнивается с каждым элементом, содержащимся в столбце.

В стандарте ANSI/ISO для языка SQL содержатся подробные правила, определяющие результаты проверки ALL, когда проверяемое значение сравнивается со столбцом результатов внутреннего запроса:

  • Если операция сравнения дает результат TRUE для каждого значения в столбце, то проверка ALL возвращает значение TRUE. Условие сравнения выполняется для каждого значения, возвращенного внутренним запросом.
  • Если операция сравнения дает результат FALSE для какогонибудь значения в столбце, то проверка ALL возвращает значение FALSE. В этом случае можно утверждать, что условие сравнения выполняется не для каждого значения, возвращенного внутренним запросом.
  • Если внутренний запрос возвращает результат в виде пустого столбца, то проверка ALL возвращает значение TRUE. Считается, что условие сравнения выполняется, даже если результаты внутренним запроса отсутствуют.
  • Если операция сравнения не дает результат FALSE ни для одного значения в столбце, но в нем присутствуют одно или несколько значений NULL, то проверка ALL возвращает значение NULL. В этой ситуации нельзя с определенностью утверждать, для всех ли значений, возвращенных внутренним запросом, справедливо условие сравнения.

Вот пример запроса с оператором ALL: вывести те товары, цена которых больше чем цена каждого монитора

SELECT *

FROM Приход

WHERE Цена > ALL (SELECT Цена

FROM Приход

WHERE Тип = 'Монитор')

Предложение WHERE внешнего запроса сравнивает значения цены каждого полученного товара со всеми ценами, полученными в результате выполнения внутреннего запроса. Если цены всех мониторов меньше проверяемого значения, то предикат >ALL возвращает значение TRUE и данный товар попадает в запрос.

Внутренний запрос определяет значения цен всех мониторов. Таблица Приход содержит пять мониторов и цены у них 9820 руб., 5820 руб., 4790 руб., 16790 руб. и 15000 руб. соответственно. Внешний запрос, используя полученные во внутреннем запросе данные, находит товары с ценой большей, чем у любого из мониторов. Например, самая высокая цена монитора – 16790 рублей. Следовательно, выбираются только те товары, у которых цена выше 16790 рублей.

Результат выполнения запроса приведен на рис. 7.

Запрос с некоррелированным подзапросом и проверкой

Рис. 7. Запрос с некоррелированным подзапросом и проверкой <ALL

В проверке ALL используются следующие операторы:

> ALL – означает: больше каждого значения элементов результирующего множества, что равносильно больше максимальной величины. Например, > ALL (1,2,3) означает больше, чем 3.

< ALL – меньше каждого значения элементов результирующего множества. Что равносильно меньше минимальной величины. Например, < ALL (1,2,3) означает меньше, чем 1.

= ALL – означает: равно всем полученным значениям.

Ключевое слово ALL используется в основном с неравенствами, чем с равенствами, так как значение может быть «равным для всех» результатом подзапроса, только если все результаты идентичны.

Посмотрите следующий запрос:

SELECT *

FROM Приход

WHERE Цена = ALL (SELECT Цена

FROM Приход

WHERE Тип = 'Монитор')

Эта команда допустима, но c данными таблицы Приход, мы не получим никакого вывода (рис. 8). Только в единственном случае вывод будет выдан этим запросом – если цены всех мониторов окажутся одинаковыми.

Запрос с некоррелированным подзапросом и проверкой

Рис. 8. Запрос с некоррелированным подзапросом и проверкой =ALL

Однако ALL может более эффективно использоваться с неравенствами, то есть с оператором <>. В SQL, выражение <> ALL в действительности соответствует » не равен любому » результату подзапроса. Другими словами, предикат верен, если данное значение не найдено среди результатов подзапроса. Следовательно, наш предыдущий пример противоположен по смыслу этому примеру:

SELECT *

FROM Приход

WHERE Цена <> ALL (SELECT Цена

FROM Приход

WHERE Тип = 'Монитор')

Внутренний подзапрос выбирает цены всех мониторов. Например, он выводит набор из пяти значений: 9820 руб., 5820 руб., 4790 руб., 16790 руб. и 15000 руб. Затем, основной запрос, выбирает все строки, с ценой, не совпадающей ни с одной из них.

Результат выполнения запроса приведен на рис. 9.

Запрос с некоррелированным подзапросом и проверкой

Рис. 9. Запрос с некоррелированным подзапросом и проверкой <>ALL

Можно сформулировать тот же самый запрос, используя, оператор NOT IN:

SELECT *

FROM Приход

WHERE Цена NOT IN (SELECT Цена

FROM Приход

WHERE Тип = 'Монитор') Можно также использовать оператор ANY:

SELECT *

FROM Приход

WHERE NOT Цена = ANY (SELECT Цена

FROM Приход

WHERE Тип = 'Монитор')

Результат будет одинаков для всех трех запросов (рис. 9). Одно значительное различие между ключевыми словами ALL и

ANY это способ действия в ситуации, когда внутренний запрос (подзапрос) не возвращает никаких значений. В этом случае проверка ALL — автоматически верна, а проверка ANY автоматически неправильна. Это означает, что в случае отсутствия в таблице Приход мониторов, следующий запрос

SELECT *

FROM Приход

WHERE Цена > ANY (SELECT Цена

FROM Приход

WHERE Тип = 'Монитор')

не произведет никакого вывода, в то время как запрос —

SELECT *

FROM Приход

WHERE Цена > ALL (SELECT Цена

FROM Приход

WHERE Тип = 'Монитор')

выведет всю таблицу Приход.

2.3. Проверка на существование

В результате проверки на существование (оператор EXISTS) можно выяснить, содержится ли в таблице результатов внутреннего запроса хотя бы одна строка.

EXISTS — это оператор, который производит значение TRUE, если в таблице результатов внутреннего запроса содержится, хотя бы одна строка, и FALSE, если таблица результатов внутреннего запроса не содержит ни одной строки.

Как видим, этот оператор производит значение логического типа. Это означает, что оператор EXISTS может работать автономно или в комбинации с другими логическими выражениями, использующими логические операторы AND, OR и NOT.

Например, запрос выводит некоторые данные из таблицы Поставщик, если один или более поставщиков в этой таблице находятся в Московской области

SELECT Код_постав, Название, Регион

FROM Поставщик

WHERE EXISTS ( SELECT *

FROM Поставщик

WHERE Регион = 'Моск.обл.' )

Результат выполнения запроса приведен на рис. 10.

Запрос с некоррелированным подзапросом и оператором EXISTS

Рис. 10. Запрос с некоррелированным подзапросом и оператором EXISTS

Внутренний запрос выбирает все данные для всех поставщиков в Московской области. Оператор EXISTS в предложении WHERE внешнего запроса отмечает, что некоторый вывод был произведен подзапросом, и принимает значение TRUE. Подзапрос (некоррелированный) был выполнен только один раз для всего внешнего запроса, и, следовательно, имеет одно значение для всех строк таблицы внешнего запроса. Поэтому все строки таблицы Поставщик будут выведены. В том случае, если в таблице Поставщик отсутствуют поставщики из Московской области, то запрос не вернет ни одной строки.

Обратите внимание на то, что оператор EXISTS возвращает значения TRUE или FALSE и не возвращает никаких данных из таблицы. По этой причине список выбора такого подзапроса часто состоит из одной звездочки *. Здесь нет необходимости указывать названия столбцов, поскольку осуществляется просто проверка существования строк, удовлетворяющих условиям, указанным в подзапросе. Однако можно и явно указать список выбора, следуя обычным правилам. Следует отметить еще одну особенность подзапроса проверки на существование — перед оператором EXISTS не должно быть названий столбцов, констант или других выражений.

Оператор EXISTS можно использовать с коррелированными подзапросами.

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

SELECT DISTINCT Ном_ном

FROM Приход A

WHERE EXISTS (SELECT *

FROM Приход B

WHERE A.Ном_док <> B.Ном_док AND A.Ном_ном = B.Ном_ном)

Результат выполнения запроса приведен на рис. 11.

Для каждой строки внешнего запроса, внутренний запрос находит строки, которые совпадают со значением поля номенклатурный номер товара Ном_ном, но со значением поля номер документа Ном_док, соответствующему другому документу. Если любые такие строки найдены внутренним запросом, это означает, что имеются два разных документа, по которым поступал один и тот же товар. Поэтому оператор EXISTS верен для текущей строки, и значение поля Ном_ном таблицы, указанной во внешнем запросе, будет выведено.

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

Запрос с коррелированным подзапросом и оператором EXISTS

Рис. 11. Запрос с коррелированным подзапросом и оператором EXISTS

В запросах может использоваться оператор NOT EXISTS.

Для оператора NOT EXISTS используются правила обработки, обратные по отношению к ключевому слову EXISTS.

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

SELECT DISTINCT Ном_ном

FROM Приход A

WHERE NOT EXISTS (SELECT *

FROM Приход B

WHERE A.Ном_док <> B.Ном_док AND A.Ном_ном = B.Ном_ном)

Если требуется в запросе вывести больше информации о товарах, а не только их номера, то можно сделать это, соединив, например, таблицу Приход с таблицей Документ

SELECT DISTINCT Ном_ном, Дата

FROM Приход A, Документ D

WHERE EXISTS (SELECT *

FROM Приход B

WHERE A.Ном_док <> B.Ном_док AND A.Ном_ном = B.Ном_ном

AND A. Ном_док = D.Ном_док)

Результат выполнения запроса приведен на рис. 12.

Запрос с комбинацией соединения и подзапроса с оператором EXISTS

Рис. 12. Запрос с комбинацией соединения и подзапроса с оператором EXISTS

Внутренний запрос здесь выглядит, как и в предыдущем примере. Внешний запрос — это соединение таблицы Приход с таблицей Документ. Новое предложение основного предиката (AND A. Ном_док = D.Ном_док), естественно, оценивается на том же самом уровне, что и предложение EXISTS.

Из-за логического оператора AND, оба условия в предложении WHERE внешнего запроса должны быть верны для того, чтобы весь предикат был верен. Следовательно, результаты подзапроса имеют смысл только в тех случаях, когда вторая часть запроса верна, а соединение — выполняемо. Таким образом, комбинация соединения и подзапроса может стать очень мощным способом обработки данных.

3. Подзапросы в предложении FROM

Подзапросы – это мощное и гибкое средство создания и комбинирования таблиц результатов запроса. Чтобы сделать подзапросы еще более универсальными, стандарт SQL-92 разрешает использовать их практически везде, где в запросах допускаются ссылки на таблицы. В частности, запрос может указываться вместо имени таблицы в предложении FROM.

Пример: вывести названия и общую сумму поставок для всех поставщиков.

SELECT Название AS ‘Название поставщика’, Общая_поставка

FROM Поставщик А, (SELECT Код_постав, SUM(Цена * Количество) AS Общая_поставка

FROM Приход, Документ

WHERE Приход. Ном_док = Документ. Ном_док GROUP BY Код_постав) В

WHERE А. Код_постав = В. Код_постав

Результат выполнения запроса приведен на рис. 13.

Результат запроса с подзапросом в предложении FROM

Рис. 13. Результат запроса с подзапросом в предложении FROM

Если первая спецификация в предложении FROM – это, как обычно, имя таблицы, то вторая спецификация – вовсе не имя таблицы, а полноценный запрос. Фактически последний может быть намного сложнее.

Когда запрос включается в предложение FROM, СУБД сначала выполняет этот запрос и создает временную таблицу его результатов, а затем выполняет внешний запрос, используя в нем созданную таблицу в качестве источника данных точно так же, как она использовала бы обычную таблицу, хранящуюся в базе данных. В нашем примере содержимым временной таблицы является информация, извлеченная из таблиц Приход и Документ, и к этой информации присоединяется таблица Поставщик для получения названий поставщиков.

Этот же запрос можно записать и другим способом. Например, соединить три таблицы Поставщик, Приход и Документ с группировкой результатов. В чем и состоит прелесть SQL. Он дает пользователям несколько способов получения одного и того же результата.

4. Правила формирования подзапросов

Теперь, когда у вас есть представление о подзапросах, суммируем некоторые правила, по которым они должны строиться. В основном они относятся к списку выбора подзапроса с некоторыми дополнительными ограничениями на функции, которые в них можно использовать:

    • Список выбора внутреннего запроса, начинающийся с оператора сравнения или с IN, может включать только одно выражение или имя столбца. При этом столбец, который указывается в предложении WHERE внешнего запроса, должен быть совместимым со столбцом, имя которого задается в списке выбора подзапроса.
    • Список выбора подзапроса, начинающийся с ключевого слова EXISTS, почти всегда включает звездочку (*). Связано это с тем, что в данном случае нет необходимости приводить имена столбцов, поскольку выполняется лишь проверка на существование (или отсутствие) любых строк, которые удовлетворяют заданным критериям.
    • Внутренние запросы, начинающиеся простым оператором сравнения (после которого нет ключевого слова ANY или ALL), не могут включать предложения GROUP BY и HAVING.
    • Типы данных ntext, text и image не могут быть использованы в списке выбора вложенных запросов.
    • Подзапросы не могут манипулировать своими результатами внутри себя. Другими словами, подзапрос не может включать предложение ORDER BY