Найти в Дзене
ГАУС IT

Подзапросы - это просто!

В этой статье мы разберем одну из важных тем это подзапросы. С помощью SQL вы можете вкладывать запросы внутрь друг друга. Обычно, внутренний запрос генерирует значение, которое проверяется в предикате внешнего запроса, определяющего, верно оно или нет. Например, предположим что мы знаем имя продавца «Миша», но не знаем значение его поля number, и хотим извлечь все данные из таблицы TABLE_1. Имеется один способ чтобы сделать это: SELECT * FROM TABLE_1 WHERE number = (SELECT number FROM PAYER WHERE name = 'Миша'); Чтобы оценить внешний (или же по другому основной) запрос, SQL сначала должен оценить внутренний запрос (или подзапрос) внутри предложения WHERE. Он делает это так как и должен делать запрос имеющий единственную цель — отыскать через таблицу PAYER все строки, где поле name равно значению Миша, и затем извлечь значения поля number этих строк. Единственной найденной строкой, естественно, будет number = 1004. Однако SQL не просто выдает это значение, а помещает его в предикат ос
Оглавление

В этой статье мы разберем одну из важных тем это подзапросы.

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

Например, предположим что мы знаем имя продавца «Миша», но не знаем значение его поля number, и хотим извлечь все данные из таблицы TABLE_1. Имеется один способ чтобы сделать это:

SELECT *
FROM TABLE_1
WHERE number = (SELECT number FROM PAYER WHERE name = 'Миша');

Чтобы оценить внешний (или же по другому основной) запрос, SQL сначала должен оценить внутренний запрос (или подзапрос) внутри предложения WHERE. Он делает это так как и должен делать запрос имеющий единственную цель — отыскать через таблицу PAYER все строки, где поле name равно значению Миша, и затем извлечь значения поля number этих строк.

Единственной найденной строкой, естественно, будет number = 1004. Однако SQL не просто выдает это значение, а помещает его в предикат основного запроса вместо самого подзапроса, так чтобы предиката прочитал, что

WHERE number = 1004

Основной запрос затем выполняется как обычно с вышеупомянутыми результатами. Конечно же, подзапрос должен выбрать один и только один столбец, а тип данных этого столбца должен совпадать с тем значением, с которым он будет сравниваться в предикате. Часто, как показано выше, выбранное поле и его значение будут иметь одинаковые имена (в этом случае, number), но это необязательно. Конечно, если бы мы уже знали номер продавца Миша, мы могли бы просто напечатать

WHERE number = 1004

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

Значения, которые могут выдавать подзапросы

Скорее всего было бы удобнее, чтобы наш подзапрос в предыдущем примере возвращал одно и только одно значение. Имея выбранным поле number "WHERE city = ‘London’" вместо "WHERE name = ‘Миша’, можно получить несколько различных значений. Это может сделать уравнение в предикате основного запроса невозможным для оценки верности или неверности, и команда выдаст ошибку.

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

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

SELECT *
FROM TABLE_1
WHERE number = ( SELECT number FROM PAYER WHERE city = ‘London’);

Поскольку мы имеем только одного продавца в ‘London’— ‘Миша’, то подзапрос будет выбирать одиночное значение number и следовательно будет принят. Но это — только в данном случае. Большинство SQL баз данных имеют многочисленных пользователей, и если другой пользователь добавит нового продавца из ‘London’ в таблицу, подзапрос выберет два значения, и ваша команда потерпит неудачу.

Если же вы хотите найти по множественному выбору то используйте за место равенства IN про него мы рассказывали в 5-ой статье.

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

Вы должны обратить внимание, что предикаты, включающие подзапросы, используют выражение <скалярная форма> <оператор> <подзапрос>, а не <подзапрос> <оператор> <скалярное выражение> или, <подзапрос> <оператор> <подзапрос>.

Другими словами, вы не должны записывать предыдущий пример так:

SELECT *
FROM TABLE_1
WHERE (
SELECT DISTINCT number
FROM TABLE_1
WHERE cnum = 2001
) = number;

Использование агрегатных функций в подзапросах

Один тип функций, который автоматически может производить одиночное значение для любого числа строк, конечно же, — агрегатная функция. Любой запрос, использующий одиночную функцию агрегата без предложения GROUP BY, будет выбирать одиночное значение для использования в основном предикате.

Например:

SELECT *
FROM TABLE_1
WHERE amt > (
SELECT AVG (amt)
FROM TABLE_1
WHERE odate = 10/04/1990
);

Имейте в виду, что сгруппированные агрегатные функции, которые являются агрегатными функциями определенными в терминах предложения GROUP BY, могут производить многочисленные значения. Они не позволительны в подзапросах такого характера. Даже если GROUP BY и HAVING используются таким способом, что только одна группа выводится с помощью подзапроса, команда будет отклонена в принципе. Вы должны использовать одиночную агрегатную функцию с предложением WHERE, что устранит нежелательные группы. Например, следующий запрос, который должен найти среднее значение комиссионных продавца в Лондоне —

SELECT AVG (comm)
FROM PAYER
GROUP BY city
HAVlNG city = "London";

не может использоваться в подзапросе! Во всяком случае это не лучший способ формировать запрос. Другим способом может быть:

SELECT AVG (comm)
FROM PAYER
WHERE city = "London";

Использование подзапросов, которые выдают много строк с помощью оператора IN

Вы можете использовать подзапросы, которые производят любое число строк если вы используете специальный оператор IN (операторы BETWEEN, LIKE, и IS NULL не могут использоваться с подзапросами). Как вы помните, IN определяет набор значений, одно из которых должно совпадать с другим термином уравнения предиката в порядке, чтобы предикат был верным. Когда вы используете IN с подзапросом, SQL просто формирует этот набор из вывода подзапроса.

SELECT *
FROM TABLE_1
WHERE number IN (
SELECT number
FROM PAYER
WHERE city = "LONDON"
);

Конечно, вы можете также использовать оператор IN, даже когда вы уверены, что подзапрос произведет одиночное значение. В любой ситуации где вы можете использовать реляционный оператор сравнения (=), вы можете использовать IN. В отличие от реляционных операторов, IN не может заставить команду потерпеть неудачу если больше чем одно значение выбрано подзапросом. Это может быть или преимуществом или недостатком.

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

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

SELECT onum, amt, odate
FROM TABLE_1
WHERE number = (
SELECT number
FROM TABLE_2
WHERE cnum = 2001
);

Вы можете устранить потребность в DISTINCT, используя IN вместо (=), подобно этому:

SELECT onum, amt, odate
FROM TABLE_1
WHERE number IN (
SELECT number
FROM TABLE_2
WHERE cnum = 2001
);

Что случится, если есть ошибка и один из порядков был аккредитован к различным продавцам? Версия, использующая IN, будет давать вам все порядки для обоих продавцов. Нет никакого очевидного способа наблюдения за ошибкой, и поэтому сгенерированные отчеты или решения, сделанные на основе этого запроса, не будут содержать ошибки. Вариант, использующий (=), просто потерпит неудачу.

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

Вы должны затем выполнять поиск неисправности, выполнив этот подзапрос отдельно и наблюдая значения, которые он производит.

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

Подзапросы выбирают одиночные столбцы

Смысл всех подзапросов обсужденных в этой статье тот, что все они выбирают одиночный столбец. Это обязательно, поскольку выбранный вывод сравнивается с одиночным значением. Подтверждением этому то, что SELECT * не может использоваться в подзапросе. Имеется исключение из этого, когда подзапросы используются с оператором EXISTS.

Использование выражений в подзапросах

Вы можете использовать выражение основанное на столбце, а не просто сам столбец, в предложении SELECT подзапроса. Это может быть выполнено или с помощью реляционных операторов или с IN. Например, следующий запрос использует реляционный оператор =:

SELECT *
FROM TABLE_1
WHERE cnum = (
SELECT number + 1000
FROM TABLE_2
WHERE name = “Serres”
);

Подзапросы в предложении HAVING

Вы можете также использовать подзапросы внутри предложения HAVING. Эти подзапросы могут использовать свои собственные агрегатные функции если они не производят многочисленных значений или использовать GROUP BY или HAVING. Следующий запрос является этому примером :

SELECT rating, COUNT ( DISTINCT cnum )
FROM TABLE_1
GROUP BY rating
HAVING rating > (
SELECT AVG (rating)
FROM TABLE_2
WHERE city = “Barcelona”
);

Подведём итог

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

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

#it #sql #обучение #курс