Пример 13: запросы на объединение и подзапросы с условием IN
Oracle Исследование 2.2.3.EXP.A
1-- Запрос 1: использование JOIN
2SELECT DISTINCT "s_id",
3 |
|
"s_name" |
4 |
|
FROM "subscribers" |
5JOIN "subscriptions"
6ON "s_id" = "sb_subscriber"
1-- Запрос 2: использование IN (... DISTINCT ...)
2SELECT "s_id",
3"s_name"
4 |
|
FROM |
"subscribers" |
|
5 |
|
WHERE |
"s_id" IN (SELECT |
DISTINCT "sb_subscriber" |
6 |
|
|
FROM |
"subscriptions") |
|
|
|
|
|
1-- Запрос 3: использование IN
2SELECT "s_id",
3"s_name"
4 |
|
FROM |
"subscribers" |
|
5 |
|
WHERE |
"s_id" IN (SELECT |
"sb_subscriber" |
6 |
|
|
FROM |
"subscriptions") |
1-- Запрос 4: использование LEFT JOIN
2SELECT "s_id",
3"s_name"
4 |
|
FROM |
"subscribers" |
|
|
5 |
|
|
LEFT JOIN |
"subscriptions" |
|
6 |
|
|
ON |
"s_id" = |
"sb_subscriber" |
7 |
|
WHERE |
"sb_subscriber" IS |
NULL |
|
|
|
|
|
|
|
1-- Запрос 5: использование NOT IN (... DISTINCT ...)
2SELECT "s_id",
3"s_name"
4 |
|
FROM |
"subscribers" |
|
|
5 |
|
WHERE |
"s_id" NOT IN |
(SELECT |
DISTINCT "sb_subscriber" |
6 |
|
|
|
FROM |
"subscriptions") |
|
|
|
|
|
|
1-- Запрос 6: использование NOT IN
2SELECT "s_id",
3"s_name"
|
4 |
|
FROM |
"subscribers" |
|
|
|
|
|
|
5 |
|
WHERE |
"s_id" NOT IN (SELECT |
"sb_subscriber" |
|
|
||
|
6 |
|
|
|
FROM |
"subscriptions") |
|
|
|
|
|
|
Медианы времени, затраченного на выполнение каждого запроса: |
|
|||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MySQL |
MS SQL Server |
Oracle |
|
|
|
JOIN |
|
|
91.065 |
8.574 |
1.136 |
|
||
|
IN (… DISTINCT …) |
0.068 |
6.711 |
0.298 |
|
||||
|
IN |
|
|
0.039 |
6.723 |
0.309 |
|
||
|
LEFT JOIN |
|
45.788 |
8.695 |
0.284 |
|
|||
|
NOT IN (… DISTINCT …) |
45.564 |
7.437 |
0.329 |
|
||||
|
NOT IN |
|
46.020 |
7.384 |
0.311 |
|
|||
Перед проведением исследования мы ставили два вопроса, и теперь у нас есть ответы:
•Влияет ли на производительность наличие DISTINCT в подзапросе (для решений с IN)?
o В случае с IN наличие DISTINCT ощутимо замедляет работу MySQL и немного ускоряет работу MS SQL Server и Oracle.
o В случае с NOT IN наличие DISTINCT немного ускоряет работу MySQL
инемного замедляет работу MS SQL Server и Oracle.
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 90/545
Пример 13: запросы на объединение и подзапросы с условием IN
•Что работает быстрее — JOIN или IN (в обоих случаях: когда мы ищем как читателей, бравших книги, так и не бравших)?
o JOIN работает медленнее IN.
oLEFT JOIN работает немного медленнее NOT IN в MySQL и MS SQL Server и немного быстрее NOT IN в Oracle.
Поскольку большинство результатов крайне близки по значениям, однозначный вывод получается только один: IN работает быстрее JOIN, в остальных случаях стоит проводить дополнительные исследования.
Задание 2.2.3.TSK.A: показать список книг, которые когда-либо были взяты читателями.
Задание 2.2.3.TSK.B: показать список книг, которые никто из читателей никогда не брал.
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 91/545
Пример 14: нетривиальные случаи использования условия IN и запросов на объединение
2.2.4.Пример 14: нетривиальные случаи использования условия IN и запросов на объединение
Существуют задачи, которые на первый взгляд решаются очень просто. Однако оказывается, что простое и очевидное решение является неверным.
Задача 2.2.4.a{92}: показать список читателей, у которых сейчас на руках нет книг (использовать JOIN).
Задача 2.2.4.b{95}: показать список читателей, у которых сейчас на руках нет книг (не использовать JOIN).
В задачах 2.2.3.* примера 13{82} всё было просто: если в таблице subscriptions есть информация о читателе, значит, он брал книги в библиотеке, а если нет
— не брал. Теперь же нас будут интересовать как читатели, никогда не бравшие книги (объективно у них на руках нет книг), так и читатели, бравшие книги (но ктото вернул всё, что брал, а кто-то ещё что-то читает).
|
Ожидаемый результат 2.2.4.a. |
|
|
|
|
s_id |
s_name |
|
1 |
Иванов И.И. |
|
2 |
Петров П.П. |
|
|
Ожидаемый результат 2.2.4.b. |
|
|
|
|
s_id |
s_name |
|
1 |
Иванов И.И. |
|
2 |
Петров П.П. |
|
Решение 2.2.4.a{92}.
MySQL Решение 2.2.4.a
1SELECT `s_id`,
2`s_name`
3 |
|
FROM |
`subscribers` |
|
4 |
|
|
LEFT OUTER JOIN |
`subscriptions` |
5 |
|
|
ON |
`s_id` = `sb_subscriber` |
6 |
|
GROUP |
BY `s_id` |
|
7 |
|
HAVING |
COUNT(IF(`sb_is_active` = 'Y', `sb_is_active`, NULL)) = 0 |
|
|
|
|
|
|
MS SQL Решение 2.2.4.a
1SELECT [s_id],
2[s_name]
3 |
|
FROM |
[subscribers] |
|
|
4 |
|
|
LEFT OUTER |
JOIN [subscriptions] |
|
5 |
|
|
|
|
ON [s_id] = [sb_subscriber] |
6 |
|
GROUP |
BY [s_id], |
|
|
7 |
|
|
[s_name] |
|
|
8 |
|
HAVING |
COUNT(CASE |
|
|
9 |
|
|
WHEN |
[sb_is_active] = 'Y' THEN [sb_is_active] |
|
10 |
|
|
ELSE |
NULL |
|
11 |
|
|
END) |
= |
0 |
|
|
|
|
|
|
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 92/545
Пример 14: нетривиальные случаи использования условия IN и запросов на объединение
Oracle Решение 2.2.4.a
1SELECT "s_id",
2"s_name"
3 |
|
FROM |
"subscribers" |
|
|
4 |
|
|
LEFT OUTER |
JOIN "subscriptions" |
|
5 |
|
|
|
|
ON "s_id" = "sb_subscriber" |
6 |
|
GROUP |
BY "s_id", |
|
|
7 |
|
|
"s_name" |
|
|
8 |
|
HAVING |
COUNT(CASE |
|
|
9 |
|
|
WHEN |
"sb_is_active" = 'Y' THEN "sb_is_active" |
|
10 |
|
|
ELSE |
NULL |
|
11 |
|
|
END) |
= |
0 |
|
|
|
|
|
|
Почему получается такое нетривиальное решение? Строки 1-5 запросов 2.2.4.a возвращают следующие данные (добавим поле sb_is_active для наглядности):
s_id |
s_name |
sb_is_active |
1 |
Иванов И.И. |
N |
1 |
Иванов И.И. |
N |
1 |
Иванов И.И. |
N |
1 |
Иванов И.И. |
N |
1 |
Иванов И.И. |
N |
2 |
Петров П.П. |
NULL |
3 |
Сидоров С.С. |
Y |
3 |
Сидоров С.С. |
Y |
3 |
Сидоров С.С. |
Y |
4 |
Сидоров С.С. |
N |
4 |
Сидоров С.С. |
Y |
4 |
Сидоров С.С. |
Y |
Признаком того, что читатель вернул все книги, является отсутствие (COUNT(…) = 0) у него записей со значением sb_is_active = 'Y'. Проблема в том, что мы не можем «заставить» COUNT считать только значения Y — он будет учитывать любые значения, не равные NULL. Отсюда легко следует вывод, что нам осталось превратить в NULL любые значения поля sb_is_active, не равные Y, т.е. получить такой результат:
s_id |
s_name |
sb_is_active |
1 |
Иванов И.И. |
NULL |
1 |
Иванов И.И. |
NULL |
1 |
Иванов И.И. |
NULL |
1 |
Иванов И.И. |
NULL |
1 |
Иванов И.И. |
NULL |
2 |
Петров П.П. |
NULL |
3 |
Сидоров С.С. |
Y |
3 |
Сидоров С.С. |
Y |
3 |
Сидоров С.С. |
Y |
4 |
Сидоров С.С. |
NULL |
4 |
Сидоров С.С. |
Y |
4 |
Сидоров С.С. |
Y |
Именно за это действие отвечают выражения IF в строке 7 запроса для MySQL и CASE в строках 8-11 запросов для MS SQL Server и Oracle — любое значение поля sb_is_active, отличное от Y, они превращают в NULL.
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 93/545
Пример 14: нетривиальные случаи использования условия IN и запросов на объединение
Теперь остаётся проверить результат работы COUNT: если он равен нулю, у читателя на руках нет книг.
Типичной ошибкой при решении задачи 2.2.4.a является попытка получить нужный результат следующим запросом:
MySQL |
Решение 2.2.4.a (ошибочный запрос) |
1SELECT `s_id`,
2`s_name`
|
3 |
|
FROM |
`subscribers` |
|
|
4 |
|
|
|
LEFT OUTER JOIN `subscriptions` |
|
5 |
|
|
|
ON `s_id` = `sb_subscriber` |
|
6 |
|
WHERE |
`sb_is_active` = 'Y' |
|
|
7 |
|
|
|
OR `sb_is_active` IS NULL |
|
8 |
|
GROUP |
BY `s_id` |
|
|
9 |
|
HAVING COUNT(`sb_is_active`) = 0 |
||
|
|
|
Решение 2.2.4.a (ошибочный запрос) |
||
|
MS SQL |
|
|||
1SELECT [s_id],
2[s_name]
|
3 |
|
FROM |
[subscribers] |
|
|
4 |
|
|
|
LEFT OUTER JOIN [subscriptions] |
|
5 |
|
|
|
ON [s_id] = [sb_subscriber] |
|
6 |
|
WHERE |
[sb_is_active] = 'Y' |
|
|
7 |
|
|
|
OR [sb_is_active] IS NULL |
|
8 |
|
GROUP |
BY [s_id], [s_name], [sb_is_active] |
|
|
9 |
|
HAVING |
COUNT([sb_is_active]) = 0 |
|
|
|
|
Решение 2.2.4.a (ошибочный запрос) |
||
|
Oracle |
|
|||
1SELECT "s_id",
2"s_name"
3 |
|
FROM "subscribers" |
|
4 |
|
LEFT OUTER JOIN |
"subscriptions" |
5 |
|
ON |
"s_id" = "sb_subscriber" |
6WHERE "sb_is_active" = 'Y'
7OR "sb_is_active" IS NULL
|
8 |
|
GROUP |
BY "s_id", "s_name", "sb_is_active" |
||
|
9 |
|
HAVING |
COUNT("sb_is_active") = 0 |
||
|
|
|
|
Здесь получается такой неверный набор данных: |
||
|
|
|
||||
s_id |
s_name |
|
||||
2 |
|
|
Петров П.П. |
|
||
Это решение учитывает никогда не бравших книги читателей (sb_is_active IS NULL), а также тех, у кого есть на руках хотя бы одна книга (sb_is_active = 'Y'), но те, кто вернул все книги, под это условие не подходят:
s_id |
|
s_name |
|
sb_is_active |
|
|
|
|
1 |
|
|
Иванов И.И. |
|
N |
|
|
|
1 |
|
|
Иванов И.И. |
|
N |
Эти записи не удовлетво- |
||
1 |
|
|
Иванов И.И. |
|
N |
ряют условию |
WHERE |
и бу- |
1 |
|
|
Иванов И.И. |
|
N |
дут пропущены. |
||
1 |
|
|
Иванов И.И. |
|
N |
|
|
|
2 |
|
|
Петров П.П. |
|
NULL |
|
|
|
3 |
|
|
Сидоров С.С. |
|
Y |
|
|
|
3 |
|
|
Сидоров С.С. |
|
Y |
|
|
|
3 |
|
|
Сидоров С.С. |
|
Y |
|
|
|
4 |
|
|
Сидоров С.С. |
|
N |
|
|
|
4 |
|
|
Сидоров С.С. |
|
Y |
|
|
|
4 |
|
|
Сидоров С.С. |
|
Y |
|
|
|
Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 94/545