Sql запросы с вложенными запросами. Вложенные и связанные подзапросы в SQL, предикат EXISTS. Подзапрос в условии EXISTS

В прошлом уроке мы столкнулись с одним неудобством. Когда мы хотели узнать, кто создал тему "велосипеды", и делали соответствующий запрос:

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

В SQL предусмотрена возможность объединять такие запросы в один путем превращения одного из них в подзапрос (вложенный запрос). Итак, чтобы узнать, кто создал тему "велосипеды", мы сделаем следующий запрос:

То есть, после ключевого слова WHERE , в условие мы записываем еще один запрос. MySQL сначала обрабатывает подзапрос, возвращает id_author=2, и это значение передается в предложение WHERE внешнего запроса.

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

Давайте для закрепления составим еще один запрос, узнаем, какие сообщения на форуме оставлял автор темы "велосипеды":

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

Давайте разберемся, как это работает.

  • Сначала MySQL выполнит самый глубокий запрос:

  • Полученный результат (id_author=2) передаст во внешний запрос, который примет вид:

  • Полученный результат (id_topic:4,1) передаст во внешний запрос, который примет вид:

  • И выдаст окончательный результат (topic_name: о рыбалке, о рыбалке). Т.е. автор темы "велосипеды" оставлял сообщения в теме "О рыбалке", созданной Сергеем (id=1) и в теме "О рыбалке", созданной Светой (id=4).
Вот собственно и все, что хотелось сказать о вложенных запросах. Хотя, есть два момента, на которые стоит обратить внимание:
  • Не рекомендуется создавать запросы со степенью вложения больше трех. Это приводит к увеличению времени выполнения и к сложности восприятия кода.
  • Приведенный синтаксис вложенных запросов, скорее наиболее употребительный, но вовсе не единственный. Например, мы могли бы вместо запроса

    написать

    Т.е. мы можем использовать любые операторы, используемые с ключевым словом WHERE (их мы изучали в прошлом уроке).

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

Виды условий поиска:
. Сравнение с результатом вложенного запроса (=, >=)
. Проверка на принадлежность результатам подзапроса (IN)
. Проверка на существование (EXISTS)
. Многократное (количественное) сравнение (ANY, ALL)

Примечания по вложенным запросам:
. Подзапрос должен выбирать только один столбец (за исключением подзапроса с предикатом EXISTS), и тип данных его результата должен соответствовать типу данных значения, указанному в предикате.
. В ряде случаев можно использовать ключевое слово DISTINCT для гарантии получения единственного значения.
. Во вложенном запросе нельзя включать раздел ORDER BY и UNION.
. Подзапрос может находиться и лева и справа от условия поиска.
. В подзапросах могут использоваться функции агрегирования без раздела GROUP BY, которые автоматически выдают специальное значение для любого количества строк, специальный предикат IN, а также выражения, основанные на столбцах.
. По возможности следует вместо подзапросов использовать соединение таблиц JOIN.

Примеры на вложенные запросы :

SELECT * FROM Orders WHERE SNum=(SELECT SNum FROM SalesPeople WHERE SName=’Motika’)
SELECT * FROM Orders WHERE SNum IN (SELECT SNum FROM SalesPeople WHERE City=’London’)
SELECT * FROM Orders WHERE SNum=(SELECT DISTINCT SNum FROM Orders WHERE CNum=2001)
SELECT * FROM Orders WHERE Amt>(SELECT AVG(Amt) FROM Orders WHERE Odate=10/04/1990)
SELECT * FROM Customer WHERE CNum=(SELECT SNum+1000 FROM SalesPeople WHERE SName=’Serres’)

2) Связанные подзапросы

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

Примеры на связанные подзапросы:

SELECT * FROM SalesPeople Main WHERE 1(SELECT AVG(Amt) FROM Orders O2 WHERE O2.CNum=O1.CNum) //возвращает все заказы, величина которых превосходит среднюю величины заказа для данного покупателя

3) Предикат EXISTS

Синтаксическая форма: EXISTS ()

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

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

Примеры на предикат EXISTS:

SELECT * FROM Customer WHERE EXISTS(SELECT * FROM Customer WHERE City=’San Jose’) – возвращает всех покупателей, если кто-то из них проживает в San Jose.
SELECT DISTINCT SNum FROM Customer First WHERE NOT EXISTS (SELECT * FROM Customer Send WHERE Send.SNum=First.SNum AND Send.CNumFirst.CNum) – возвращает номера продавцов, обслуживших только одного покупателя.
SELECT DISTINCT F.SNum, SName, F.City FROM SalesPeople F, Customer S WHERE EXISTS (SELECT * FROM Customer T WHERE S.SNum=T.SNum AND S.CNumT.CNum AND F.SNum=S.SNum) – возвращает номера, имена и города проживания всех продавцов, обслуживших нескольких покупателей.
SELECT * FROM SalesPeople Frst WHERE EXISTS (SELECT * FROM Customer Send WHERE Frst.SNum=Send.SNum AND 1

4) Предикаты количественного сравнения

Синтаксическая форма: {=|>|=|} ANY|ALL ()

Эти предикаты используют в качестве аргумента подзапрос, однако, по сравнению с предикатом EXISTS, они применяются в конъюнкции с предикатами отношения (=,>=). В этом смысле они сходны с предикатом IN, но применяются только с подзапросами. Стандарт допускает использовать вместо ANY ключевое слово SOME, однако не все СУБД его поддерживают.

Примечания по предикатам сравнения:
. Предикат ALL принимает значение TRUE, если каждое значение, выбранное в процессе выполнения подзапроса, удовлетворяет условию, заданному в предикате внешнего запроса. Чаще всего он используется с неравенствами.
. Предикат ANY принимает значение TRUE, если хотя бы одно значение, выбранное в процессе выполнения подзапроса, удовлетворяет условию, заданному в предикате внешнего запроса. Чаще всего он используется с неравенствами.
. Если подзапрос не возвращает строк, то ALL автоматически принимает значение TRUE (считается, что условие сравнения выполняется), а для ANY – FALSE.
. Если сравнение не имеет значения TRUE ни для одной строки и есть одна или несколько строк с NULL значением, то ANY возвращает UNKNOWN.
. Если сравнение не имеет значения FALSE ни для одной строки и есть одна или несколько строк с NULL значением, то ALL возвращает UNKNOWN.

Примеры на предикат количественного сравнения:

SELECT * FROM SalesPeople WHERE City=ANY(SELECT City FROM Customer)
SELECT * FROM Orders WHERE Amt ALL(SELECT Rating FROM Customer WHERE City=’Rome’)

5) Предикат уникальности

UNIQUE|DISTINCT ()

Предикат служит для проверка уникальности (отсутствия дублей) в выходных данных подзапроса. Причем в предикате UNIQUT строки с NULL значениями считаются уникальными, а в предикате DISTINCT два неопределенных значения считаются равными друг другу.

6) Предикат совпадений

MATCH ()

Предикат MATCH проверяет, будет ли значение строки запроса совпадать со значением любой строки, полученной в результате подзапроса. От предикатов IN И ANY такой подзапрос отличается тем, что позволяет обрабатывать «частичные» (PARTIAL) совпадения, которые могут встречаться среди строк, имеющих часть NULL-значений.

7) Запросы в разделе FROM

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

SELECT CName, Tot_Amt FROM Customer, (SELECT CNum, SUM(Amt) AS Tot_Amt FROM Orders GROUP BY CNum) WHERE City=’London’ AND Customer.CNum=Orders.CNum
//подзапрос возвращает суммарную величину заказов, сделанных каждым покупателем из Лондона.

8) Рекурсивные запросы

WITH RECURSIVE
Q1 AS SELECT … FROM … WHERE …
Q2 AS SELECT … FROM … WHERE …


Вложенные запросы (подзапросы) в SQL Oracle

1.Цели лабораторной работы

    Изучить возможности SQL Oracle по формулировке и обработке подзапросов.

    Приобрести практический опыт по формулировке и обработке подзапросов с использованием SQL*Plus.

2.Теоретические основы

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

Подзапросы используются для:

    определения множества строк, который должны быть вставлены в целевую таблицу в предложениях INSERT или CREATE TABLE;

    определения одного или более значений, присваиваемых существующим строка в предложении UPDATE;

    получения значений для фраз WHERE, HAVING или START WITH в предложениях SELECT, UPDATE, и DELETE;

    определения значений указанного столбца в списке INSERT ... VALUES;

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

Это производится путем размещения подзапроса во фразе FROM соответствующего запроса как если бы это было именем таблицы. Вы можете также использовать таким образом подзапросы вместо таблиц в предложениях INSERT, UDPATE и DELETE.

Используемые таким образом подзапросы могут использовать переменные связывания (correlation variables), однако только такие, которые определены только в самом подзапросе, ссылки на внешние переменные не допустимы. Внешние ссылки (подзапросы с левой корреляцией - left-correlated subqueries) допустимы только во фразе FROM предложения SELECT .

Подзапрос дает ответ на содержательные запросы, имеющие сложную структуру. Например, для определения, кто работает на кафедре Иванова, вы сначала используете подзапрос для определения кафедры, на которой работает Иванов, а затем отвечаете на основной запрос путем формулировки предложения SELECT.

Подзапрос может содержать другие подзапросы. Oracle не ограничивает глубину вложенности подзапросов.

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

Oracle выполняет корреляционные (связанный) подзапрос , когда подзапрос ссылается на столбец таблицы внешнего запроса. Связанный подзапрос вычисляется для каждой строки, обрабатываемой внешним предложением. Внешним предложением может быть SELECT, UPDATE или DELETE.

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

2.1.Подзапрос во фразе WHERE

2.1.1.Подзапрос в простом условии сравнения

Синтаксис:

Описание:

При использовании простых условий сравнения с подзапросом во фразе WHERE применяются следующие правила:

    Подзапрос должен возвращать единственную строку.

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

    Если левая часть является списком выражений (expr_list ), то подзапрос должен возвращать единственную строку со списком значений, который соответствует по количеству и типу значениям из expr_list . В этом случае оператор сравнения дает TRUE, если каждое значение в expr_list равно (в случае =) или не равно (в случае!=, ^=,) каждому значение, возвращаемому подзапросом.

Примеры :

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

WHERE Building = (SELECT Building

WHERE UPPER(Name) = "INFORMATICS");

2. Выбрать факультеты, чьи фонды меньше фонда кафедры CAD:

WHERE UPPER(Name) = "CAD");

3. Выбрать преподавателей, у которых salary + commission превышает более чем на 100 половину salary + commission преподавателя Bill:

WHERE Salary + Commission + 100 > (SELECT (Salary + Commission) / 2

WHERE UPPER(Name) = "BILL");

4. Выбрать преподавателей, которые работают на той же кафедре, что и Bill и занимают ту же должность, что и Bill:

WHERE (DepNo, Post) = (SELECT DepNo, Post

WHERE UPPER(Name) = "BILL");

2.1.2.Подзапрос в условии сравнения групп

Синтаксис:

Описание:

При использовании условий сравнения групп с подзапросом во фразе WHERE применяются следующие правила:

    Подзапрос может возвращать ноль или более строк.

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

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

ANY и SOME эквивалентны и сравнивают значение слева с каждым значением списка справа, возвращаемого подзапросом. Подзапрос может вернуть ноль или более строк. Условие равно TRUE, если по крайней мере одна строка подзапроса удовлетворяет условию (соответствует оператору сравнения) по отношению к значению (списку значений) определенному левым операндом, в противном получаем FALSE. Если подзапрос не возвращает строк, то получаем FALSE.

ALL сравнивают значение слева с каждым значением списка справа, возвращаемого подзапросом. Дает TRUE, если ВСЕ строки, возвращаемые подзапросом, удовлетворяют условию (соответствуют оператору сравнения) по отношению к значению (списку значений) определенному левым операндом, в противном получаем FALSE. Если подзапрос не возвращает строк, то получаем TRUE

Примеры:

1. Выдать кафедры, фонд которых больше фонда по крайней мере одного из факультетов:

WHERE Fund >

ANY, ALL и агрегатные функции . Обратите внимание, что левое значение меньше, чем максимальное значение из множества, задаваемого правым операндом”, а оператор >ANY эквивалентен следующему утверждению “левое значение больше, чем минимальное значение из множества, задаваемого правым операндом ”. Поэтому операторы ANY могут быть выражены через функции MAX и MIN в подзапросе. В свою очередь, ALL) эквивалентно утверждению: «левое значение меньше (больше), чем минимальное (максимальное) значение из множества, задаваемого правым операндом ».

2. Выдать кафедры, фонд которых больше фонда по крайней мере одного из факультетов:

WHERE Fund > ANY (SELECT Fund FROM FACULTY);

WHERE Fund > (SELECT MIN(Fund) FROM FACULTY);

WHERE Rating >ALL (SELECT Rating

FROM SGROUP, DEPARTMENT

WHERE SGROUP.DepNo = DEPARTMENT.DepNo AND

UPPER(DEPARTMENT.Name) = "DBMS" AND SGROUP.Course = 5);

2.1.3.Подзапрос в условии проверки вхождения элемента во множество

Синтаксис:

Описание:

Это условие в таком синтаксисе проверят вхождение элемента (списка элементов) во множество (множество списков), создаваемое подзапросом.

Пример:

1. Выбрать преподавателей, которые имеют лекции по крайней мере одному такому предмету, по которым читает лекции преподаватель Bill:

FROM TEACHER T, LECTURE L

WHERE T.TchNo = L.TchNo AND

SbjNo IN (SELECT SbjNo

FROM TEACHER TCH, LECTURE LEC

WHERE TCH.TchNo = LEC.TchNo AND UPPER(TCH.Name) = "BILL");

2.1.4.Подзапрос в условии EXISTS

Синтаксис:

Описание:

Дает TRUE, если подзапрос возвращает по крайней мере одну строку.

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

2.2.Связанные подзапросы

Для того, чтобы связать подзапрос с внешним запросом (предложением), необходимо в подзапросе была ссылка на столбец внешнего запроса. Подзапрос вычисляется для каждой строки, обрабатываемой внешним запросом (предложением). В качестве внешнего предложения могут выступать SELECT, UPDATE или DELETE.

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

SELECT select_list

FROM table1 t_alias1

WHERE expr operator

(SELECT column_list

FROM table2 t_alias2

WHERE t_alias1.column operator t_alias2.column);

UPDATE table1 t_alias1

FROM table2 t_alias2

DELETE FROM table1 t_alias1

WHERE column operator

FROM table2 t_alias2

WHERE t_alias1.column = t_alias2.column);

2.2.1.Связанные подзапросы во фразе WHERE

Примеры:

1. Выдать преподавателей, которые имеют по крайней мере одну лекцию:

WHERE EXISTS (SELECT *

Здесь в условии LECTURE.TchNo = TEACHER.TchNo подзапроса мы ссылаемся на внешний запрос. Поэтому подзапрос является связанным.

2. Выдать преподавателей, которые не имеют ни одной лекции:

WHERE NOT EXISTS (SELECT *

WHERE LECTURE.TchNo = TEACHER.TchNo);

2.3.Простые и связанные подзапросы во фразе HAVING

Вы можете использовать простые и связанные подзапросы во фразе HAVING.

Если вы используете связанный подзапрос в фразе HAVING, то в подзапросе можно ссылаться на те столбцы внешнего запроса, которые могут использоваться в фразе HAVING (обычно это столбцы, по которым производится группирование).

Примеры:

1. Перечислить факультеты, у которых сумма фондов финансирования всех их кафедр превышает более чем на 20000 фонд финансирования той кафедры факультета, которая имеет максимальный фонд.

FROM FACULTY F1, DEPARTMENT D1

WHERE F1.FacNo = D1.FacNo

GROUP BY F1.Name

HAVING SUM(D1.Fund) > (SELECT 200000 + MAX(D2.Fund)

FROM FACULTY F2, DEPARTMENT D2

WHERE F2.FacNo = D2.FacNo AND F1.Name = F2.Name);

Вложенный запрос относится к специальным объектам в программе 1С. К его функционалу прибегают, если требуется сформировать и выполнить специальные виды запросов к таблицам базы данных (БД) в программе. Для получения результата по запросу сначала необходимо корректное составление текста запроса, который будет аккумулировать актуальную информацию об источниках для получения данных. Это могут быть таблицы, специальные поля, уникальные группировки.

Современный язык запросов для программы 1С

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

  • Разыменование ссылочных полей;
  • Возможности нетипичных запросов;
  • Упорядочивание по примитивным типам;
  • Использование конструкции «Представление»;
  • Выборка данных по шаблону;
  • Совместное использование группировок и итогов;
  • Конструкция с условием «Где».

Есть и другие отличительные особенности языка запросов в 1С, которые позволяют ему быть эффективным.

Что нужно знать о вложенных запросах и языке запросов?

Профессиональное изучение конструкции языка запроса позволяет не только понять, для чего предназначен язык запросов , но и научится представлять совокупность объектов базы в формате 2-х мерных таблиц, обрабатывать консоль запросов, создавать файлы для хранения списков запросов, познакомиться со специфическими полями таблиц, имеющих ссылочный тип. И всё это будут только начальные знания о системе вложенных запросов в 1С. Важно освоить и групповые операции, относящиеся к языку запросов, изучить, как делать корректную выборку по нескольким источникам данных.

Пример вложенного запроса

Рассмотрим один из вариантов вложенного запроса на основе объединения запросов. Предположим, есть документы по приходу и расходу, при этом одно и то же юридическое лицо выступает и как продавец, и как покупатель. Нам нужно узнать, общий долг по контрагенту. Для этого используем эффективную конструкцию «ОБЪЕДИНИТЬ ВСЕ».

Таблица «ПРИХОД»

Таблица «РАСХОД»

Сначала определяем все расходы, затем приход по юрлицам. Второй запрос ставим со знаком «-», это позволит корректно свернуть данные.

Результат действия:

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

Тогда результатом отчёта будет:

Когда речь идёт о таблицах Расхода и Прихода, к примеру, по справочнику Номенклатура, то необходимо исключить дублирование. Но сливать каждый из запросов отдельно не получится. Поэтому нужно сделать так:

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

Существуют определённые требования, которых необходимо придерживаться, выполняя объединение двух запросов. Прежде всего, это касается количества полей. Необходимо, чтобы их число было идентичным. К примеру, если бы в таблице данных «Расход» была указана Скидка, а в данных по Приходу такого элемента не было бы, то следует прибегнуть к следующей конструкции:

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

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

Кстати, при применении конструкции Объединения следует отличать её от понятия соединения нескольких запросов. Объединение даёт возможность вертикального соединения результатов выборки, поочерёдно берутся данные из первого, потом уже переходят ко второму. Именно так получаются данные в результате конструкции объединения информации из двух таблиц.