alan_beaulieu-learning_sql-ru (865932), страница 21
Текст из файла (страница 21)
Как видно по результатам, четыре строки из таблицы business включены дважды (ID клиентов 10, 11, 12 и 13).Поскольку вряд ли вы когданибудь дважды включите один и тот жезапрос в составной запрос, вот другой пример составного запроса, покоторому возвращаются дублирующие данные:mysql> SELECT emp_id> FROM employee> WHERE assigned_branch_id = 2> AND (title = 'Teller' OR title = 'Head Teller')> UNION ALL> SELECT DISTINCT open_emp_id> FROM account> WHERE open_branch_id = 2;++| emp_id |++|10 ||11 ||12 ||10 |++4 rows in set (0.01 sec)Первый запрос составного выражения выбирает всех операционистовотделения Woburn, а второй возвращает другое множество – операционистов, открывавших счета в отделении Woburn.
Из четырех строк реОператоры работы с множествами115зультирующего набора одна дублируется (ID сотрудника – 10). Еслибы потребовалось исключить дублирующие строки из составной таблицы, вместо оператора union all надо было бы использовать операторunion:mysql> SELECT emp_id> FROM employee> WHERE assigned_branch_id = 2> AND (title = 'Teller' OR title = 'Head Teller')> UNION> SELECT DISTINCT open_emp_id> FROM account> WHERE open_branch_id = 2;++| emp_id |++|10 ||11 ||12 |++3 rows in set (0.01 sec)Для данной версии запроса с применением оператора union all в результирующий набор включаются только три разные строки, а не четыре(три разные, одна дублирующаяся).Оператор intersectСпецификация SQL ANSI включает оператор intersect (пересечение),предназначенный для выполнения пересечений.
К сожалению, MySQLверсии 4.1 не реализует оператор intersect. Oracle (но не SQL Server) позволяет использовать intersect. Однако поскольку для всех примеровданной книги используется MySQL, результирующие наборы для примеров запросов в данном разделе являются вымышленными и не могутбыть получены в MySQL до версии 5.0 включительно. Здесь не показано приглашение MySQL (mysql>), потому что эти выражения не выполняются сервером MySQL.Если два запроса составного запроса возвращают неперекрывающиесятаблицы, пересечением будет пустое множество. Рассмотрим следующий запрос:SELECT emp_id, fname, lnameFROM employeeINTERSECTSELECT cust_id, fname, lnameFROM individual;Empty set (0.04 sec)Первый запрос возвращает ID и имя каждого сотрудника, а второй – IDи имя каждого клиента.
Это абсолютно неперекрывающиеся множества, поэтому пересечение двух этих множеств и дает пустое множество.116Глава 6. Работа с множествамиВторой шаг – выявить два запроса, действительно имеющих перекрывающиеся данные, и затем применить оператор intersect. Для этого используем тот же запрос, что и для демонстрации разницы междуunion и union all, только на этот раз возьмем оператор intersect:SELECT emp_idFROM employeeWHERE assigned_branch_id = 2AND (title = 'Teller' OR title = 'Head Teller')INTERSECTSELECT DISTINCT open_emp_idFROM accountWHERE open_branch_id = 2;++| emp_id |++|10 |++1 row in set (0.01 sec)Пересечение этих двух запросов дает сотрудника с ID равным 10, чтоявляется единственным значением, имеющимся в результирующихнаборах обоих запросов.Наряду с оператором intersect, удаляющим все дублирующие строкиобласти перекрытия, спецификация SQL ANSI предлагает оператор intersect all, не удаляющий дубликаты.
Единственный сервер БД, в настоящее время реализующий оператор intersect all, – DB2 UniversalServer компании IBM.Оператор exceptСпецификация SQL ANSI включает оператор except (разность), предназначенный для выполнения операции разности. Опять же, к сожалению, MySQL версии 4.1 не реализует оператор except, поэтому в данном разделе действуют те же соглашения, что и в предыдущем.При работе с Oracle Database вам понадобится использовать оператор minus (минус), не совместимый со спецификацией ANSI.Операция except возвращает первую таблицу за вычетом всех перекрытий со второй таблицей.
Вот пример из предыдущего раздела, но с оператором except вместо intersect:SELECT emp_idFROM employeeWHERE assigned_branch_id = 2AND (title = 'Teller' OR title = 'Head Teller')EXCEPTSELECT DISTINCT open_emp_idFROM accountОператоры работы с множествами117WHERE open_branch_id = 2;++| emp_id |++|11 ||12 |++2 rows in set (0.01 sec)В этом варианте запроса результирующий набор включает три строкииз результирующего набора первого запроса минус сотрудник с ID,равным 10, который присутствует в результирующих наборах обоихзапросов. В спецификации SQL ANSI также описан оператор except all,но опять же он реализован только в DB2 Universal Server IBM.В операторе except all есть небольшая хитрость. Вот пример, показывающий, как обрабатываются дублирующие данные.
Скажем, есть двамножества данных, имеющих следующий вид:Множество A++| emp_id |++|10 ||11 ||12 ||10 ||10 |++Множество B++| emp_id |++|10 ||10 |++В результате операции A except B получаем следующее:++| emp_id |++|11 ||12 |++Если изменить операцию и применить A except all B, увидим следующее:++| emp_id |++|10 ||11 |118Глава 6. Работа с множествами|12 |++Следовательно, разница между этими двумя операциями в том, что except удаляет все экземпляры дублирующихся данных из множества А,тогда как except all удаляет из множества А только один экземпляр дубликата данных для каждого экземпляра дубликата данных множества В.Правила операций с множествамиВ следующих разделах обозначены некоторые правила, которых необходимо придерживаться при работе с составными запросами.Результаты сортирующего составного запросаЕсли требуется сортировать результаты составного запроса, после последнего входящего в него запроса можно добавить блок order by.
В блоке order by указываются имена столбцов из первого запроса составногозапроса. До сих пор в каждом примере главы имена столбцов в обоихзапросах составного запроса совпадали, но так делать не обязательно,что и показывает следующий пример:mysql> SELECT emp_id, assigned_branch_id> FROM employee> WHERE title = 'Teller'> UNION> SELECT open_emp_id, open_branch_id> FROM account> WHERE product_cd = 'SAV'> ORDER BY emp_id;+++| emp_id | assigned_branch_id |+++|1 |1 ||7 |1 ||8 |1 ||9 |1 ||10 |2 ||11 |2 ||12 |2 ||14 |3 ||15 |3 ||16 |4 ||17 |4 ||18 |4 |+++12 rows in set (0.04 sec)В этом примере в двух запросах заданы разные имена столбцов. Еслив блоке order by указать имя столбца из второго запроса, будет получена следующая ошибка:Правила операций с множествами119mysql> SELECT emp_id, assigned_branch_id> FROM employee> WHERE title = 'Teller'> UNION> SELECT open_emp_id, open_branch_id> FROM account> WHERE product_cd = 'SAV'> ORDER BY open_emp_id;ERROR 1054 (42S22): Unknown column 'open_emp_id' in 'order clause'Чтобы избежать этой проблемы, рекомендуется в обоих запросах давать столбцам одинаковые псевдонимы.Старшинство операций с множествамиЕсли в составном запросе больше двух запросов, использующих разныеоператоры работы с множествами, то для обеспечения желаемых результатов следует продумать порядок расположения этих запросов в составном выражении.
Рассмотрим следующее составное выражение изтрех запросов:mysql> SELECT cust_id> FROM account> WHERE product_cd IN ('SAV', 'MM')> UNION ALL> SELECT a.cust_id> FROM account a INNER JOIN branch b> ON a.open_branch_id = b.branch_id> WHERE b.name = 'Woburn Branch'> UNION> SELECT cust_id> FROM account> WHERE avail_balance BETWEEN 500 AND 2500;++| cust_id |++|1 ||2 ||3 ||4 ||8 ||9 ||7 ||11 ||5 |++9 rows in set (0.00 sec)Этот составной запрос включает три запроса, возвращающих набор неуникальных ID клиентов.
Первые два запроса разделены операторомunion all, а второй и третий – оператором union. Может показаться, чторасположение операторов union и union all не играет роли, но на самом120Глава 6. Работа с множествамиделе разница есть. Вот тот же составной запрос, в котором эти операторы поменялись местами:mysql> SELECT cust_id> FROM account> WHERE product_cd IN ('SAV', 'MM')> UNION> SELECT a.cust_id> FROM account a INNER JOIN branch b> ON a.open_branch_id = b.branch_id> WHERE b.name = 'Woburn Branch'> UNION ALL> SELECT cust_id> FROM account> WHERE avail_balance BETWEEN 500 AND 2500;++| cust_id |++|1 ||2 ||3 ||4 ||8 ||9 ||7 ||11 ||1 ||1 ||2 ||3 ||3 ||4 ||4 ||5 ||9 |++17 rows in set (0.00 sec)При виде результатов становится очевидным, что порядок разных операторов работы с множествами в составном запросе действительноимеет значение.
В общем, составные запросы из трех или больше запросов оцениваются в порядке сверху вниз, но с учетом следующих пояснений:• По спецификации SQL ANSI из всех операторов работы с множествами первым выполняется оператор intersect.• Порядок сочетания запросов можно задавать с помощью скобок.Но поскольку в MySQL еще не реализованы ни оператор intersect, нискобки в составных запросах, для получения нужного результата придется аккуратно расставлять запросы, образующие составной запрос.При использовании другого сервера БД, для переопределения порядкаУпражнения121обработки составных запросов по умолчанию (сверху вниз) запросы,расположенные рядом, можно заключить в скобки:(SELECT cust_idFROM accountWHERE product_cd IN ('SAV', 'MM')UNION ALLSELECT a.cust_idFROM account a INNER JOIN branch bON a.open_branch_id = b.branch_idWHERE b.name = 'Woburn Branch')INTERSECT(SELECT cust_idFROM accountWHERE avail_balance BETWEEN 500 AND 2500EXCEPTSELECT cust_idFROM accountWHERE product_cd = 'CD'AND avail_balance < 1000);Для этого составного запроса первый и второй запросы комбинируются оператором union all, затем третий и четвертый запросы – оператором except, и, наконец, для формирования окончательного результирующего набора результаты этих двух операций комбинируются с помощью оператора intersect.УпражненияСледующие упражнения призваны протестировать понимание операций с множествами.
Ответы на эти упражнения приведены в приложении С.6.1Имеются множество A = {L M N O P} и множество B = {P Q R S T}. Какие множества будут получены в результате следующих операций:••••A union BA union all BA intersect BA except B6.2Напишите составной запрос для выбора имен и фамилий всех клиентовфизических лиц, а также имен и фамилий всех сотрудников.6.3Отсортируйте результаты упражнения 6.2 по столбцу lname.Создание, преобразованиеи работа с даннымиКак говорилось в предисловии, цель данной книги – показать универсальные методы SQL, применяемые на разных серверах БД.