2. Содержание
• Что такое оператор соединения?
• CROSS JOIN
• INNER JOIN
• OUTER JOIN
• Множественные соединения
• Вопросы-ответы
3. Оператор соединения
- оператор алгебры, две таблицы на входе, таблица
на выходе
- результат широкий, т.к. содержит все колонки
исходных таблиц
- соединение происходит по условию
- количество строк в результате меняется
4. Пример, INNER JOIN
Users Payments
UserID UserName
1 Ivan
2 Pit
3 John
UserID Date Amount
1 01.01.2013 1000
1 01.02.2013 2000
2 01.01.2013 1000
UserID UserName UserID Date Amount
1 Ivan 1 01.01.2013 1000
1 Ivan 1 01.01.2013 2000
2 Pit 2 01.01.2013 1000
6. CROSS JOIN
Вырожденый случай соединения, без условия. То же
самое, что декартово перемножение.
A = {1, 2}
B = {A, B, C}
A CROSS JOIN B
A x B = {(1,A), (1,B), (1, C), (2,A), (2,B), (2, C)}
Опасная штука, т.к. может создавать результаты
огромных размеров.
7. INNER JOIN
Выводит совпадающие по заданому условию строки из
двух таблиц. Условия можно писать в WHERE и в ON,
нет практической разницы где это делать.
SELECT * FROM Users JOIN Payments
ON Users.UserID = Payments.UserID
WHERE Users.UserID = 1
SELECT * FROM Users JOIN Payments
ON Users.UserID = Payments.UserID
AND Users.UserID = 1
8. INNER JOIN
Общее правило - в ON мы пишем условия для
соединения таблиц, в WHERE - все остальные условия
SELECT * FROM Users JOIN Payments
ON Users.UserID = Payments.UserID
WHERE Users.UserID = 1
9. OUTER JOIN
Необходимость таких соединений становится понятна
если попытаться написать классический запрос вида
"вернуть всех пользователей без платежей".
Внешнее соединение возвращает все строки из одной
таблицы, плюс добавляет строки из другой таблицы,
если выполняется условие соединения. В результате у
нас как минимум столько же строк как в одной таблице.
Есть LEFT JOIN, RIGHT JOIN и FULL JOIN. LEFT JOIN -
все строки из левой таблицы,RIGHT JOIN - все строки
из правой, FULL - все строки из обоих таблиц.
10. Пример, OUTER JOIN
Users Payments
UserID UserName
1 Ivan
2 Pit
3 John
UserID Date Amount
1 01.01.2013 1000
1 01.02.2013 2000
2 01.01.2013 1000
UserID UserName UserID Date Amount
1 Ivan 1 01.01.2013 1000
1 Ivan 1 01.01.2013 2000
2 Pit 2 01.01.2013 1000
3 John NULL NULL NULL
11. Пример, OUTER JOIN
SELECT Users.*
FROM Users
LEFT JOIN Payments
ON Users.UserID = Payments.UserID
WHERE
Payments.UserID IS NULL
12. OUTER JOIN, трюк 1
• Есть разница, где писать условия, в
WHERE или ON. То, что написано в ON
"выполнится" до соединения, в WHERE -
после. Поэтому проверку на IS NULL
можно написать только в WHERE, в этот
момент NULL уже есть в результате
соединения.
WHERE
Payments.UserID IS NULL
13. OUTER JOIN, трюк 2
• Часто нужно сначала отфильтровать
правую таблицу (для LEFT JOIN), а потом
соединять. В этом случае условие можно
записать в ON.
SELECT Users.*
FROM Users LEFT JOIN Payments
ON Users.UserID = Payments.UserID
and Payments.Date = '2013-01-10'
WHERE
Payments.UserID IS NULL
14. OUTER JOIN, трюк 3
• Условие к левой таблице в ON будет
проигнорировано, таков алгоритм LEFT
JOIN, он всегда вернет все записи из
левой таблицы
FROM Users LEFT JOIN Payments
ON Users.UserID = Payments.UserID
and Users.UserID = 1
15. OUTER JOIN, трюк 4.1
• Если используется LEFT JOIN и проверка на IS
NULL, то условия в WHERE не должны
конфликтовать. Сервер ничего не вычисляя
возвращает NULL в этом случае:
SELECT Users.*
FROM Users
LEFT JOIN Payments
ON Users.UserID = Payments.UserID
WHERE
Payments.UserID IS NULL
and Payments.Date = '2013-01-10'
16. OUTER JOIN, трюк 4.2
• Менее очевидный случай. Запрос
работает так, словно условия UserID IS NULL
нет вообще.
FROM Users LEFT JOIN Payments
ON Users.UserID = Payments.UserID
WHERE
(Payments.UserID IS NULL
or Payments.Date = '2013-01-10')
and Payments.Amount > 1000
17. OUTER JOIN, трюк 4.2
Так и получается, если упростить
логическое выражение
(Payments.UserID IS NULL
or Payments.Date = '2013-01-10')
and Payments.Amount > 1000
A = Payments.UserID IS NULL
B = Payments.Date = '2013-01-10
C = Payments.Amount > 1000
(A + B)*C = A*C + B*C
A*C = Payments.UserID IS NULL and Payments.Amount > 1000 == FALSE
18. Множественные соединения
Три и более таблицы во FROM.
Правило 1: INNER JOIN безопасны с точки
зрения логики.
Порядок соединения определяется
оптимизатором, записывать можно в
произвольном порядке.
A JOIN B JOIN C = C JOIN B JOIN A
19. Множественные соединения
Правило 2: OUTER JOIN коварны
Обрабатываются в порядке записи.
Классическая задача - вернуть данные из зависимых
таблиц для всех пользователей. Интуитивное решение:
FROM Users
LEFT JOIN Payments
ON Users.UserID = Payments.UserID
JOIN PaymentDetail
ON Payments.PaymentID = ...
20. Множественные соединения
В результате не будет пользователей, у которых нет
платежей, т.к. соединения выполняются попарно, после
первого соединения они будут в результате, после
второго - исчезнут, т.к. у них все поля из Payments -
NULL, в том числе те, по которым присоединяется
третья таблица.
Сервер знает об этом, и обрабатывает такое
соединение как INNER JOIN.
21. Множественные соединения
Интуитивное решение:
FROM Users
LEFT JOIN Payments
ON Users.UserID = Payments.UserID
LEFT JOIN PaymentDetail
ON Payments.PaymentID = ...
Работает, но не эффективно, и неправильно, если есть
платежи без деталей. Мы хотели информацию по всем
пользователям независимо от наличия платежей,
платежи без деталей нам не интересны.
22. Множественные соединения
Можно поставить OUTER JOIN последним оператором:
FROM Payments
JOIN PaymentDetail
ON Payments.PaymentID = ...
RIGHT JOIN Users
ON Users.UserID = Payments.UserID
Если запрос сложный, то это может быть непросто.
Более наглядно использовать скобки.
23. Множественные соединения
FROM Users
LEFT JOIN
(Payments JOIN PaymentDetail
ON Payments.PaymentID = ...
)
ON Users.UserID = Payments.UserID
Скобки в такой записи не обязательны, они нужны
только для облегчения понимания логики запроса. На
самом деле важен только порядок, в котором записаны
ON
24. Множественные соединения
FROM Users
LEFT JOIN Payments
JOIN PaymentDetail
ON Payments.PaymentID = ...
ON Users.UserID = Payments.UserID
Порядок записи ON не произвольный, это chiatric
relation. Первый - последний, второй - предпоследний,
третий - третий с конца и т.д.