Главная страница

изучаем SQL. Она позволяет решать многошаговые задачи одним выражением


Скачать 1.6 Mb.
НазваниеОна позволяет решать многошаговые задачи одним выражением
Дата09.02.2018
Размер1.6 Mb.
Формат файлаpdf
Имя файлаизучаем SQL.pdf
ТипДокументы
#36127
страница21 из 31
1   ...   17   18   19   20   21   22   23   24   ...   31
Подзапросы в условиях фильтрации
Во многих примерах данной главы подзапросы используются как вы ражения в условиях фильтрации, поэтому для вас не будет сюрпри зом, что это одно из основных применений подзапросов. Но условия фильтрации, использующие подзапросы, встречаются не только в бло ке where. Например, следующий запрос использует блок having для по иска сотрудника, открывшего наибольшее количество счетов:
mysql> SELECT open_emp_id, COUNT(*) how_many
> FROM account
> GROUP BY open_emp_id
> HAVING COUNT(*) = (SELECT MAX(emp_cnt.how_many)
> FROM (SELECT COUNT(*) how_many
> FROM account
> GROUP BY open_emp_id) emp_cnt);
+
+
+
| open_emp_id | how_many |
+
+
+
| 1 | 8 |
+
+
+
1 row in set (0.01 sec)
Подзапрос блока having находит максимальное число счетов, откры тых одним сотрудником, а основной запрос находит сотрудника, от крывшего это количество счетов. Если бы с наибольшим числом от

190
Глава 9. Подзапросы крытых счетов были связаны несколько сотрудников, запрос возвра тил бы несколько строк.
Подзапросы как генераторы выражений
В этом последнем разделе главы я завершу тему, с которой начал, –
скалярные подзапросы, возвращающие один столбец и одну строку.
Кроме условий фильтрации скалярные подзапросы применимы везде,
где может появляться выражение, включая блоки select и order by за проса и блок values (значения) выражения insert.
Ранее в этой главе, в разделе «Подзапросы, ориентированные на зада чи», было показано, как с помощью подзапроса отделить механизм группировки от остального запроса. Вот вариант того же запроса, ис пользующий подзапросы с той же целью, но по другому:
mysql> SELECT
> (SELECT p.name FROM product p
> WHERE p.product_cd = a.product_cd
> AND p.product_type_cd = 'ACCOUNT') product,
> (SELECT b.name FROM branch b
> WHERE b.branch_id = a.open_branch_id) branch,
> (SELECT CONCAT(e.fname, ' ', e.lname) FROM employee e
> WHERE e.emp_id = a.open_emp_id) name,
> SUM(a.avail_balance) tot_deposits
> FROM account a
> GROUP BY a.product_cd, a.open_branch_id, a.open_emp_id;
+
+
+
+
+
| product | branch | name | tot_deposits |
+
+
+
+
+
| NULL | Woburn Branch | Paula Roberts | 9345.55 |
| NULL | So. NH Branch | Theresa Markham | 0.00 |
| certificate of deposit | Headquarters | Michael Smith | 11500.00 |
| certificate of deposit | Woburn Branch | Paula Roberts | 8000.00 |
| checking account | Headquarters | Michael Smith | 782.16 |
| checking account | Woburn Branch | Paula Roberts | 3315.77 |
| checking account | Quincy Branch | John Blake | 1057.75 |
| checking account | So. NH Branch | Theresa Markham | 67852.33 |
| money market account | Headquarters | Michael Smith | 14832.64 |
| money market account | Quincy Branch | John Blake | 2212.50 |
| savings account | Headquarters | Michael Smith | 767.77 |
| savings account | Woburn Branch | Paula Roberts | 700.00 |
| savings account | So. NH Branch | Theresa Markham | 387.99 |
| NULL | Quincy Branch | John Blake | 50000.00 |
+
+
+
+
+
14 rows in set (0.01 sec)
Между этим запросом и приведенной ранее версией, использующей подзапрос в блоке from, есть два основных различия:

Вместо соединения таблиц product, branch и employee с данными сче та в блоке select используются связанные скалярные подзапросы для поиска типа счета, отделения и сотрудника.

Использование подзапросов
191

Результирующий набор содержит 14 строк, а не 11, и три типа сче тов – null.
Три дополнительные строки появляются в результирующем наборе потому, что предыдущая версия запроса включала условие фильтра ции p.product_type_cd = 'ACCOUNT'. Этот фильтр исключал строки для счетов типов INSURANCE (страховка) и LOAN (ссуда), например не большие ссуды коммерческим предприятиям. Поскольку в этой вер сии запроса нет соединения с таблицей product, нет возможности вклю чить условие фильтрации в основной запрос. Связанный подзапрос к таблице product включает этот фильтр, но единственный производи мый им эффект – указание null вместо типа счета. Если хотите изба виться от дополнительных трех строк, можно соединить таблицу product с таблицей account и включить условие фильтрации или просто сделать следующее:
mysql> SELECT all_prods.product, all_prods.branch,
> all_prods.name, all_prods.tot_deposits
> FROM
> (SELECT
> (SELECT p.name FROM product p
> WHERE p.product_cd = a.product_cd
> AND p.product_type_cd = 'ACCOUNT') product,
> (SELECT b.name FROM branch b
> WHERE b.branch_id = a.open_branch_id) branch,
> (SELECT CONCAT(e.fname, ' ', e.lname) FROM employee e
> WHERE e.emp_id = a.open_emp_id) name,
> SUM(a.avail_balance) tot_deposits
> FROM account a
> GROUP BY a.product_cd, a.open_branch_id, a.open_emp_id) all_prods
> WHERE all_prods.product IS NOT NULL;
+
+
+
+
+
| product | branch | name | tot_deposits |
+
+
+
+
+
| certificate of deposit | Headquarters | Michael Smith | 11500.00 |
| certificate of deposit | Woburn Branch | Paula Roberts | 8000.00 |
| checking account | Headquarters | Michael Smith | 782.16 |
| checking account | Woburn Branch | Paula Roberts | 3315.77 |
| checking account | Quincy Branch | John Blake | 1057.75 |
| checking account | So. NH Branch | Theresa Markham | 67852.33 |
| money market account | Headquarters | Michael Smith | 14832.64 |
| money market account | Quincy Branch | John Blake | 2212.50 |
| savings account | Headquarters | Michael Smith | 767.77 |
| savings account | Woburn Branch | Paula Roberts | 700.00 |
| savings account | So. NH Branch | Theresa Markham | 387.99 |
+
+
+
+
+
11 rows in set (0.01 sec)
Теперь, после помещения предыдущего запроса в подзапрос (названный all_prods
) и добавления условия фильтрации для исключения значений null столбца product, запрос возвращает желаемые 11 строк. В итоге по

192
Глава 9. Подзапросы лучаем запрос, где выполняется группировка только необработанных данных таблицы account, а затем результат приукрашивается с помо щью данных из трех других таблиц. И все это без всяких соединений.
Как отмечалось ранее, скалярные подзапросы тоже могут появляться в блоке order by. Следующий запрос извлекает данные сотрудников,
отсортированные по фамилиям начальников сотрудников и затем по фамилиям самих сотрудников:
mysql> SELECT emp.emp_id, CONCAT(emp.fname, ' ', emp.lname) emp_name,
> (SELECT CONCAT(boss.fname, ' ', boss.lname)
> FROM employee boss
> WHERE boss.emp_id = emp.superior_emp_id) boss_name
> FROM employee emp
> WHERE emp.superior_emp_id IS NOT NULL
> ORDER BY (SELECT boss.lname FROM employee boss
> WHERE boss.emp_id = emp.superior_emp_id), emp.lname;
+
+
+
+
| emp_id | emp_name | boss_name |
+
+
+
+
| 14 | Cindy Mason | John Blake |
| 15 | Frank Portman | John Blake |
| 9 | Jane Grossman | Helen Fleming |
| 8 | Sarah Parker | Helen Fleming |
| 7 | Chris Tucker | Helen Fleming |
| 13 | John Blake | Susan Hawthorne |
| 6 | Helen Fleming | Susan Hawthorne |
| 5 | John Gooding | Susan Hawthorne |
| 16 | Theresa Markham | Susan Hawthorne |
| 10 | Paula Roberts | Susan Hawthorne |
| 17 | Beth Fowler | Theresa Markham |
| 18 | Rick Tulman | Theresa Markham |
| 12 | Samantha Jameson | Paula Roberts |
| 11 | Thomas Ziegler | Paula Roberts |
| 2 | Susan Barker | Michael Smith |
| 3 | Robert Tyler | Michael Smith |
| 4 | Susan Hawthorne | Robert Tyler |
+
+
+
+
17 rows in set (0.01 sec)
Этот запрос использует два связанных скалярных подзапроса: один в блоке select, извлекающий полное имя руководителя каждого со трудника, а другой в блоке order by, возвращающий только фамилию руководителя сотрудника для целей сортировки.
Наряду с применением скалярных подзапросов в выражении select можно использовать несвязанные скалярные подзапросы, формирую щие значения для выражения insert. Например, предполагается соз дать новую строку счета. Предоставлены следующие данные:

Тип счета («savings account»)

Федеральный ID клиента («555 55 5555»)

Краткий обзор подзапросов
193

Название отделения, в котором был открыт счет («Quincy Branch»)

Имя и фамилия операциониста, открывшего счет («Frank Portman»)
Прежде чем можно будет создать строку в таблице account, понадобит ся найти значения ключей всех этих элементов данных, чтобы запол нить столбцы внешних ключей таблицы account. Сделать это можно двумя способами: выполнить четыре запроса для извлечения значений первичных ключей и поместить эти значения в выражение insert или получить значения четырех ключей с помощью подзапросов внутри выражения insert. Вот пример второго подхода:
INSERT INTO account
(account_id, product_cd, cust_id, open_date, last_activity_date,
status, open_branch_id, open_emp_id, avail_balance, pending_balance)
VALUES (NULL,
(SELECT product_cd FROM product WHERE name = 'savings account'),
(SELECT cust_id FROM customer WHERE fed_id = '555 55 5555'),
'2005 01 25', '2005 01 25', 'ACTIVE',
(SELECT branch_id FROM branch WHERE name = 'Quincy Branch'),
(SELECT emp_id FROM employee WHERE lname = 'Portman' AND fname = 'Frank'),
0, 0);
Единственное SQL выражение позволяет вам одновременно создать строку в таблице account и найти четыре значения столбцов внешнего ключа. Однако у этого подхода есть один недостаток. Если с помощью подзапросов заполнять столбцы, допускающие значения null, выраже ние выполнится успешно, даже если один из подзапросов не возвратит значение. Например, если в четвертом подзапросе в имени Frank Port man сделана опечатка, строка в таблице account будет все равно созда на, но столбцу open_emp_id будет присвоено значение null.
Краткий обзор подзапросов
В этой главе рассмотрено множество тем, поэтому, пожалуй, не лишне вкратце повторить их. Примеры данной главы продемонстрировали подзапросы, которые:

Возвращают один столбец и одну строку, один столбец и несколько строк, а также несколько столбцов и строк.

Не зависят от содержащего выражения (несвязанные подзапросы).

Используют один или более столбцов из содержащего выражения
(связанные подзапросы).

Применяются в условиях, используемых операторами сравнения и специальными операторами in, not in, exists и not exists.

Могут находиться в выражениях select, update, delete и insert.

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

194
Глава 9. Подзапросы

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

Используются в блоках select, from, where, having и order by запросов.
Очевидно, подзапросы – очень многогранный инструмент, поэтому не отчаивайтесь, если при первом прочтении главы вы поймете не все представленные здесь принципы. Продолжайте экспериментировать с подзапросами и вскоре обнаружите, что при написании каждого не обычного SQL выражения очень полезно рассматривать возможность использования подзапроса.
Упражнения
Эти упражнения ориентированы на проверку понимания подзапросов.
Решения приведены в приложении С.
9.1
Создайте запрос к таблице account, использующий условие фильтра ции с несвязанным подзапросом к таблице product для поиска всех кре дитных счетов (product.product_type_cd = 'LOAN'). Должны быть выбра ны ID счета, код счета, ID клиента и доступный остаток.
9.2
Переработайте запрос из упражнения 9.1, используя связанный подза прос к таблице product для получения того же результата.
9.3
Соедините следующий запрос с таблицей employee, чтобы показать уро вень квалификации каждого сотрудника:
SELECT 'trainee' name, '2004 01 01' start_dt, '2005 12 31' end_dt
UNION ALL
SELECT 'worker' name, '2002 01 01' start_dt, '2003 12 31' end_dt
UNION ALL
SELECT 'mentor' name, '2000 01 01' start_dt, '2001 12 31' end_dt
Дайте подзапросу псевдоним levels (уровни) и включите ID сотрудни ка, имя, фамилию и квалификацию (levels.name). (Совет: в условии со единения определяйте диапазон, в который попадает столбец em ployee.start_date
, с помощью условия неравенства.)
9.4
Создайте запрос к таблице employee для получения ID, имени и фами лии сотрудника вместе с названиями отдела и отделения, к которым он приписан. Не используйте соединение таблиц.

И снова соединения
На данный момент читатель должен владеть концепцией внутреннего соединения, представленной в главе 5. Основное внимание в этой главе уделено другим способам соединения таблиц, включая внешнее и пере крестное соединения.
Внешние соединения
До сих пор ни в одном из приведенных примеров, включающих запросы к нескольким таблицам, не поднимался вопрос о том, что не все строки таблицы могут соответствовать условиям соединения. Например, при соединении таблицы account с таблицей customer ничего не было сказа но о возможности отсутствия для значения столбца cust_id таблицы account соответствующего значения в столбце cust_id таблицы customer.
Если бы такое случилось, некоторые строки одной из таблиц не вошли бы в результирующий набор.
На всякий случай давайте проверим данные таблицы. Вот столбцы ac count_id и cust_id таблицы account:
mysql> SELECT account_id, cust_id
> FROM account;
+
+
+
| account_id | cust_id |
+
+
+
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 2 |
| 5 | 2 |
| 6 | 3 |
| 7 | 3 |
| 8 | 4 |
| 9 | 4 |

196
Глава 10. И снова соединения
| 10 | 4 |
| 11 | 5 |
| 12 | 6 |
| 13 | 6 |
| 14 | 7 |
| 15 | 8 |
| 16 | 8 |
| 17 | 9 |
| 18 | 9 |
| 19 | 9 |
| 20 | 10 |
| 21 | 10 |
| 22 | 11 |
| 23 | 12 |
| 24 | 13 |
+
+
+
24 rows in set (0.04 sec)
Имеется 24 счета 13 разных клиентов с ID клиента от 1 до 13, по край ней мере по одному счету на каждого. Вот множество клиентских ID
таблицы customer:
mysql> SELECT cust_id
> FROM customer;
+
+
| cust_id |
+
+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
| 10 |
| 11 |
| 12 |
| 13 |
+
+
13 rows in set (0.02 sec)
В таблице customer 13 строк с ID от 1 до 13. Таким образом, каждый ID
клиента включен в таблицу account, по крайней мере, один раз. Следо вательно, при соединении двух таблиц по столбцу cust_id можно ожи дать, что в результирующий набор будут включены все 24 строки (если нет других условий фильтрации):
mysql> SELECT a.account_id, c.cust_id
> FROM account a INNER JOIN customer c
> ON a.cust_id = c.cust_id;

Внешние соединения
197
+
+
+
| account_id | cust_id |
+
+
+
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 2 |
| 5 | 2 |
| 6 | 3 |
| 7 | 3 |
| 8 | 4 |
| 9 | 4 |
| 10 | 4 |
| 11 | 5 |
| 12 | 6 |
| 13 | 6 |
| 14 | 7 |
| 15 | 8 |
| 16 | 8 |
| 17 | 9 |
| 18 | 9 |
| 19 | 9 |
| 20 | 10 |
| 21 | 10 |
| 22 | 11 |
| 23 | 12 |
| 24 | 13 |
+
+
+
24 rows in set (0.00 sec)
Как и ожидалось, в результирующем наборе представлены все 24 сче та. Но что произойдет, если соединить таблицу account с одной из спе циализированных таблиц клиентов, например таблицей business?
mysql> SELECT a.account_id, b.cust_id, b.name
> FROM account a INNER JOIN business b
> ON a.cust_id = b.cust_id;
+
+
+
+
| account_id | cust_id | name |
+
+
+
+
| 20 | 10 | Chilton Engineering |
| 21 | 10 | Chilton Engineering |
| 22 | 11 | Northeast Cooling Inc. |
| 23 | 12 | Superior Auto Body |
| 24 | 13 | AAA Insurance Inc. |
+
+
+
+
5 rows in set (0.00 sec)
Теперь в результирующем наборе только пять строк вместо 24. Загля нем в таблицу business, чтобы понять, почему так произошло:
mysql> SELECT cust_id, name
> FROM business;

198
Глава 10. И снова соединения
+
+
+
| cust_id | name |
+
+
+
| 10 | Chilton Engineering |
| 11 | Northeast Cooling Inc. |
| 12 | Superior Auto Body |
| 13 | AAA Insurance Inc. |
+
+
+
4 rows in set (0.01 sec)
Из 13 строк таблицы клиентов только четыре относятся к юридиче ским лицам. И поскольку у одного из юридических лиц два счета, в об щей сложности с юридическими лицами связаны пять строк таблицы account
Но что делать, если требуется, чтобы запрос возвращал все счета, но при этом включал название фирмы, только если счет связан с юриди ческим лицом? Это пример, когда необходимо внешнее соединение
(outer join) таблиц account и business:
mysql> SELECT a.account_id, a.cust_id, b.name
> FROM account a LEFT OUTER JOIN business b
> ON a.cust_id = b.cust_id;
+
+
+
+
| account_id | cust_id | name |
+
+
+
+
| 1 | 1 | NULL |
| 2 | 1 | NULL |
| 3 | 1 | NULL |
| 4 | 2 | NULL |
| 5 | 2 | NULL |
| 6 | 3 | NULL |
| 7 | 3 | NULL |
| 8 | 4 | NULL |
| 9 | 4 | NULL |
| 10 | 4 | NULL |
| 11 | 5 | NULL |
| 12 | 6 | NULL |
| 13 | 6 | NULL |
| 14 | 7 | NULL |
| 15 | 8 | NULL |
| 16 | 8 | NULL |
| 17 | 9 | NULL |
| 18 | 9 | NULL |
| 19 | 9 | NULL |
| 20 | 10 | Chilton Engineering |
| 21 | 10 | Chilton Engineering |
| 22 | 11 | Northeast Cooling Inc. |
| 23 | 12 | Superior Auto Body |
| 24 | 13 | AAA Insurance Inc. |
+
+
+
+
24 rows in set (0.00 sec)

Внешние соединения
1   ...   17   18   19   20   21   22   23   24   ...   31


написать администратору сайта