Тайны оператора JOIN

2,566 views

Published on

by Oleksandr Sinitsyn

  • Be the first to comment

Тайны оператора JOIN

  1. 1. Тайны оператора JOINПроблемы и их решение
  2. 2. Содержание• Что такое оператор соединения?• CROSS JOIN• INNER JOIN• OUTER JOIN• Множественные соединения• Вопросы-ответы
  3. 3. Оператор соединения- оператор алгебры, две таблицы на входе, таблицана выходе- результат широкий, т.к. содержит все колонкиисходных таблиц- соединение происходит по условию- количество строк в результате меняется
  4. 4. Пример, INNER JOINUsers PaymentsUserID UserName1 Ivan2 Pit3 JohnUserID Date Amount1 01.01.2013 10001 01.02.2013 20002 01.01.2013 1000UserID UserName UserID Date Amount1 Ivan 1 01.01.2013 10001 Ivan 1 01.01.2013 20002 Pit 2 01.01.2013 1000
  5. 5. Пример, INNER JOINSELECT *FROM UsersJOIN PaymentsON Users.UserID = Payments.UserID
  6. 6. CROSS JOINВырожденый случай соединения, без условия. То жесамое, что декартово перемножение.A = {1, 2}B = {A, B, C}A CROSS JOIN BA x B = {(1,A), (1,B), (1, C), (2,A), (2,B), (2, C)}Опасная штука, т.к. может создавать результатыогромных размеров.
  7. 7. INNER JOINВыводит совпадающие по заданому условию строки издвух таблиц. Условия можно писать в WHERE и в ON,нет практической разницы где это делать.SELECT * FROM Users JOIN PaymentsON Users.UserID = Payments.UserIDWHERE Users.UserID = 1SELECT * FROM Users JOIN PaymentsON Users.UserID = Payments.UserIDAND Users.UserID = 1
  8. 8. INNER JOINОбщее правило - в ON мы пишем условия длясоединения таблиц, в WHERE - все остальные условияSELECT * FROM Users JOIN PaymentsON Users.UserID = Payments.UserIDWHERE Users.UserID = 1
  9. 9. OUTER JOINНеобходимость таких соединений становится понятнаесли попытаться написать классический запрос вида"вернуть всех пользователей без платежей".Внешнее соединение возвращает все строки из однойтаблицы, плюс добавляет строки из другой таблицы,если выполняется условие соединения. В результате унас как минимум столько же строк как в одной таблице.Есть LEFT JOIN, RIGHT JOIN и FULL JOIN. LEFT JOIN -все строки из левой таблицы,RIGHT JOIN - все строкииз правой, FULL - все строки из обоих таблиц.
  10. 10. Пример, OUTER JOINUsers PaymentsUserID UserName1 Ivan2 Pit3 JohnUserID Date Amount1 01.01.2013 10001 01.02.2013 20002 01.01.2013 1000UserID UserName UserID Date Amount1 Ivan 1 01.01.2013 10001 Ivan 1 01.01.2013 20002 Pit 2 01.01.2013 10003 John NULL NULL NULL
  11. 11. Пример, OUTER JOINSELECT Users.*FROM UsersLEFT JOIN PaymentsON Users.UserID = Payments.UserIDWHEREPayments.UserID IS NULL
  12. 12. OUTER JOIN, трюк 1• Есть разница, где писать условия, вWHERE или ON. То, что написано в ON"выполнится" до соединения, в WHERE -после. Поэтому проверку на IS NULLможно написать только в WHERE, в этотмомент NULL уже есть в результатесоединения.WHEREPayments.UserID IS NULL
  13. 13. OUTER JOIN, трюк 2• Часто нужно сначала отфильтроватьправую таблицу (для LEFT JOIN), а потомсоединять. В этом случае условие можнозаписать в ON.SELECT Users.*FROM Users LEFT JOIN PaymentsON Users.UserID = Payments.UserIDand Payments.Date = 2013-01-10WHEREPayments.UserID IS NULL
  14. 14. OUTER JOIN, трюк 3• Условие к левой таблице в ON будетпроигнорировано, таков алгоритм LEFTJOIN, он всегда вернет все записи излевой таблицыFROM Users LEFT JOIN PaymentsON Users.UserID = Payments.UserIDand Users.UserID = 1
  15. 15. OUTER JOIN, трюк 4.1• Если используется LEFT JOIN и проверка на ISNULL, то условия в WHERE не должныконфликтовать. Сервер ничего не вычисляявозвращает NULL в этом случае:SELECT Users.*FROM UsersLEFT JOIN PaymentsON Users.UserID = Payments.UserIDWHEREPayments.UserID IS NULLand Payments.Date = 2013-01-10
  16. 16. OUTER JOIN, трюк 4.2• Менее очевидный случай. Запросработает так, словно условия UserID IS NULLнет вообще.FROM Users LEFT JOIN PaymentsON Users.UserID = Payments.UserIDWHERE(Payments.UserID IS NULLor Payments.Date = 2013-01-10)and Payments.Amount > 1000
  17. 17. OUTER JOIN, трюк 4.2Так и получается, если упроститьлогическое выражение(Payments.UserID IS NULLor Payments.Date = 2013-01-10)and Payments.Amount > 1000A = Payments.UserID IS NULLB = Payments.Date = 2013-01-10C = Payments.Amount > 1000(A + B)*C = A*C + B*CA*C = Payments.UserID IS NULL and Payments.Amount > 1000 == FALSE
  18. 18. Множественные соединенияТри и более таблицы во FROM.Правило 1: INNER JOIN безопасны с точкизрения логики.Порядок соединения определяетсяоптимизатором, записывать можно впроизвольном порядке.A JOIN B JOIN C = C JOIN B JOIN A
  19. 19. Множественные соединенияПравило 2: OUTER JOIN коварныОбрабатываются в порядке записи.Классическая задача - вернуть данные из зависимыхтаблиц для всех пользователей. Интуитивное решение:FROM UsersLEFT JOIN PaymentsON Users.UserID = Payments.UserIDJOIN PaymentDetailON Payments.PaymentID = ...
  20. 20. Множественные соединенияВ результате не будет пользователей, у которых нетплатежей, т.к. соединения выполняются попарно, послепервого соединения они будут в результате, послевторого - исчезнут, т.к. у них все поля из Payments -NULL, в том числе те, по которым присоединяетсятретья таблица.Сервер знает об этом, и обрабатывает такоесоединение как INNER JOIN.
  21. 21. Множественные соединенияИнтуитивное решение:FROM UsersLEFT JOIN PaymentsON Users.UserID = Payments.UserIDLEFT JOIN PaymentDetailON Payments.PaymentID = ...Работает, но не эффективно, и неправильно, если естьплатежи без деталей. Мы хотели информацию по всемпользователям независимо от наличия платежей,платежи без деталей нам не интересны.
  22. 22. Множественные соединенияМожно поставить OUTER JOIN последним оператором:FROM PaymentsJOIN PaymentDetailON Payments.PaymentID = ...RIGHT JOIN UsersON Users.UserID = Payments.UserIDЕсли запрос сложный, то это может быть непросто.Более наглядно использовать скобки.
  23. 23. Множественные соединенияFROM UsersLEFT JOIN(Payments JOIN PaymentDetailON Payments.PaymentID = ...)ON Users.UserID = Payments.UserIDСкобки в такой записи не обязательны, они нужнытолько для облегчения понимания логики запроса. Насамом деле важен только порядок, в котором записаныON
  24. 24. Множественные соединенияFROM UsersLEFT JOIN PaymentsJOIN PaymentDetailON Payments.PaymentID = ...ON Users.UserID = Payments.UserIDПорядок записи ON не произвольный, это chiatricrelation. Первый - последний, второй - предпоследний,третий - третий с конца и т.д.
  25. 25. Вопросы - ответыРекомендуемая литература:Itzik Ben-GanInside Microsoft SQL Server 2008: T-SQL Querying

×