Пример 12: запросы на объединение и преобразование столбцов в строки
b_name |
a_name |
Евгений Онегин |
А.С. Пушкин |
Сказка о рыбаке и рыбке |
А.С. Пушкин |
Основание и империя |
А. Азимов |
Психология программирования |
Д. Карнеги |
Психология программирования |
Б. Страуструп |
Язык программирования С++ |
Б. Страуструп |
Курс теоретической физики |
Л.Д. Ландау |
Курс теоретической физики |
Е.М. Лифшиц |
Искусство программирования |
Д. Кнут |
Результат выполнения первой группировки и построчного объединения:
book |
author(s) |
Евгений Онегин |
А.С. Пушкин |
Сказка о рыбаке и рыбке |
А.С. Пушкин |
Основание и империя |
А. Азимов |
Психология программирования |
Б. Страуструп, Д. Карнеги |
Язык программирования С++ |
Б. Страуструп |
Курс теоретической физики |
Е.М. Лифшиц, Л.Д. Ландау |
Искусство программирования |
Д. Кнут |
На втором шаге набор данных о каждой книге и её авторах уже позволяет проводить группировку сразу по двум полям — book и author(s), т.к. списки авторов уже подготовлены, и никакая информация о них не будет потеряна:
book |
|
author(s) |
|
g_name |
Евгений Онегин |
А.С. Пушкин |
|
Классика |
|
Евгений Онегин |
А.С. Пушкин |
|
Поэзия |
|
Сказка о рыбаке и рыбке |
А.С. Пушкин |
|
Классика |
|
Сказка о рыбаке и рыбке |
А.С. Пушкин |
|
Поэзия |
|
Основание и империя |
А. Азимов |
|
Фантастика |
|
Психология программирования |
Б. Страуструп, Д. Карнеги |
Программирование |
||
Психология программирования |
Б. Страуструп, Д. Карнеги |
Психология |
||
Язык программирования С++ |
Б. Страуструп |
|
Программирование |
|
Курс теоретической физики |
Е.М. Лифшиц, |
|
Классика |
|
|
Л.Д. Ландау |
|
|
|
Искусство программирования |
Д. Кнут |
|
Классика |
|
Искусство программирования |
Д. Кнут |
|
Программирование |
|
И получается итоговый результат: |
|
|
||
|
|
|
|
|
book |
|
author(s) |
|
genre(s) |
Евгений Онегин |
|
А.С. Пушкин |
Классика, Поэзия |
|
Искусство программирования |
|
Д. Кнут |
Классика, |
|
|
|
|
Программирование |
|
Курс теоретической физики |
|
Е.М. Лифшиц, |
Классика |
|
|
|
Л.Д. Ландау |
|
|
Основание и империя |
|
А. Азимов |
Фантастика |
|
Психология программирования |
|
Б. Страуструп, |
Программирование, |
|
|
|
Д. Карнеги |
Психология |
|
Сказка о рыбаке и рыбке |
|
А.С. Пушкин |
Классика, Поэзия |
|
Язык программирования С++ |
|
Б. Страуструп |
Программирование |
|
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 80/545
Пример 12: запросы на объединение и преобразование столбцов в строки
Задание 2.2.2.TSK.A: показать все книги с их жанрами (дублирование названий книг не допускается).
Задание 2.2.2.TSK.B: показать всех авторов со всеми написанными ими книгами и всеми жанрами, в которых они работали (дублирование имён авторов, названий книг и жанров не допускается).
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 81/545
Пример 13: запросы на объединение и подзапросы с условием IN
2.2.3.Пример 13: запросы на объединение и подзапросы с условием IN
Очень часто запросы на объединение можно преобразовать в запросы с подзапросом и ключевым словом IN (обратное преобразование тоже возможно). Рассмотрим несколько типичных примеров:
Задача 2.2.3.a{83}: показать список читателей, когда-либо бравших в библиотеке книги (использовать JOIN).
Задача 2.2.3.b{84}: показать список читателей, когда-либо бравших в библиотеке книги (не использовать JOIN).
Задача 2.2.3.c{86}: показать список читателей, никогда не бравших в библиотеке книги (использовать JOIN).
Задача 2.2.3.d{87}: показать список читателей, никогда не бравших в библиотеке книги (не использовать JOIN).
Легко заметить, что пары задач 2.2.3.a-2.2.3.b и 2.2.3.c-2.2.3.d как раз являются предпосылкой к использованию преобразования JOIN в IN.
|
Ожидаемый результат 2.2.3.a. |
||
|
|
|
|
s_id |
s_name |
|
|
1 |
Иванов И.И. |
|
|
3 |
Сидоров С.С. |
|
|
4 |
Сидоров С.С. |
|
|
|
Ожидаемый результат 2.2.3.b. |
||
|
|
|
|
s_id |
s_name |
|
|
1 |
Иванов И.И. |
|
|
3 |
Сидоров С.С. |
|
|
4 |
Сидоров С.С. |
|
|
|
Ожидаемый результат 2.2.3.c. |
||
|
|
|
|
s_id |
s_name |
|
|
2 |
Петров П.П. |
|
|
|
Ожидаемый результат 2.2.3.d. |
||
|
|
||
s_id |
s_name |
|
|
2 |
Петров П.П. |
|
|
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 82/545
Пример 13: запросы на объединение и подзапросы с условием IN
Решение 2.2.3.a{83}.
|
MySQL |
Решение 2.2.3.a |
|||
|
1 |
|
SELECT |
DISTINCT `s_id`, |
|
|
2 |
|
|
|
`s_name` |
|
3 |
|
FROM |
`subscribers` |
|
4JOIN `subscriptions`
5ON `s_id` = `sb_subscriber`
|
MS SQL |
Решение 2.2.3.a |
|||
|
1 |
|
SELECT |
DISTINCT [s_id], |
|
|
2 |
|
|
|
[s_name] |
|
3 |
|
FROM |
[subscribers] |
|
4JOIN [subscriptions]
5ON [s_id] = [sb_subscriber]
|
Oracle |
|
Решение 2.2.3.a |
||
|
1 |
|
SELECT |
DISTINCT "s_id", |
|
|
2 |
|
|
|
"s_name" |
|
3 |
|
FROM |
"subscribers" |
|
4JOIN "subscriptions"
5ON "s_id" = "sb_subscriber"
Для всех трёх СУБД это решение полностью эквивалентно. Важную роль здесь играет ключевое слово DISTINCT, т.к. без него результат будет вот таким (в силу того факта, что JOIN найдёт все случаи выдачи книг каждому из читателей, а нам по условию задачи важен просто факт того, что человек хотя бы раз брал книгу):
s_id |
s_name |
1 |
Иванов И.И. |
1 |
Иванов И.И. |
1 |
Иванов И.И. |
1 |
Иванов И.И. |
1 |
Иванов И.И. |
3 |
Сидоров С.С. |
3 |
Сидоров С.С. |
3 |
Сидоров С.С. |
4 |
Сидоров С.С. |
4 |
Сидоров С.С. |
4 |
Сидоров С.С. |
Но у DISTINCT есть и опасность. Представьте, что мы решили не извлекать идентификатор читателя, ограничившись его именем (приведём пример только для MySQL, т.к. в MS SQL Server и Oracle ситуация совершенно идентична):
MySQL Решение 2.2.3.a (демонстрация потенциальной проблемы)
1 |
|
SELECT |
DISTINCT `s_name` |
2 |
|
FROM |
`subscribers` |
3JOIN `subscriptions`
4ON `s_id` = `sb_subscriber`
Запрос вернул следующие данные:
s_name
Иванов И.И.
Сидоров С.С.
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 83/545
Пример 13: запросы на объединение и подзапросы с условием IN
В правильном ожидаемом результате было три записи (в т.ч. двое Сидоровых с разными идентификаторами). Сейчас же эта информация утеряна.
Решение 2.2.3.b{82}.
MySQL Решение 2.2.3.b
1SELECT `s_id`,
2`s_name`
3 |
|
FROM |
`subscribers` |
|
4 |
|
WHERE |
`s_id` IN (SELECT |
DISTINCT `sb_subscriber` |
5 |
|
|
FROM |
`subscriptions`) |
MS SQL Решение 2.2.3.b
1SELECT [s_id],
2[s_name]
3 |
|
FROM |
[subscribers] |
|
4 |
|
WHERE |
[s_id] IN (SELECT |
DISTINCT [sb_subscriber] |
|
|
|
|
|
5 |
|
|
FROM |
[subscriptions]) |
Oracle Решение 2.2.3.b
1SELECT "s_id",
2"s_name"
3 |
|
FROM |
"subscribers" |
|
4 |
|
WHERE |
"s_id" IN (SELECT |
DISTINCT "sb_subscriber" |
5 |
|
|
FROM |
"subscriptions") |
Снова решение для всех трёх СУБД совершенно одинаково. Слово DISTINCT в подзапросе (в 4-й строке всех трёх запросов) используется для того, чтобы СУБД приходилось анализировать меньший набор данных. Будет ли разница в производительности существенной, мы рассмотрим сразу после решения следующих двух задач.
А пока покажем графически, как работают эти два запроса. Начнём с варианта с JOIN. СУБД перебирает значения s_id из таблицы subscribers и проверяет, есть ли в таблице subscriptions записи, поле sb_subscriber которых содержит то же самое значение:
Таблица |
subscribers |
|
Таблица |
subscriptions |
|||
s_id |
|
s_name |
|
|
sb_subscriber |
|
|
1 |
Иванов И.И. |
|
|
1 |
|
|
|
2 |
Петров П.П. |
|
|
1 |
|
|
|
3 |
Сидоров С.С. |
|
|
3 |
|
|
|
4 |
Сидоров С.С. |
|
|
1 |
|
|
|
|
|
|
|
4 |
|
|
|
|
|
|
|
|
1 |
|
|
|
|
|
|
|
3 |
|
|
|
|
|
|
|
3 |
|
|
|
|
|
|
|
4 |
|
|
|
|
|
|
|
1 |
|
|
|
|
|
|
|
4 |
|
|
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 84/545