ІННЕР ПРИЄДНАЙТЕСЬ до пункту «ДЕ»


941

Для простоти припустимо, що всі відповідні поля є NOT NULL.

Ви можете зробити:

SELECT
    table1.this, table2.that, table2.somethingelse
FROM
    table1, table2
WHERE
    table1.foreignkey = table2.primarykey
    AND (some other conditions)

Інакше:

SELECT
    table1.this, table2.that, table2.somethingelse
FROM
    table1 INNER JOIN table2
    ON table1.foreignkey = table2.primarykey
WHERE
    (some other conditions)

Чи працюють ці двоє однаково MySQL?




18
Якщо я правильно зрозумів, перший варіант - це неявний синтаксис ANSI SQL-89, а другий варіант - синтаксис явного приєднання ANSI SQL-92. Обидва отримають однаковий результат у відповідності реалізації SQL, і обидва отримають однаковий план запитів у добре виконаних реалізаціях SQL. Я особисто віддаю перевагу синтаксису SQL-89, але багато людей віддають перевагу синтаксису SQL-92.
Mikko Rantalainen

11
@Hogan Я вказував на офіційні назви різних синтаксисів. Жодна з відповідей не чітко прописала повні імена, тому я вирішив додати їх як коментарі. Однак мій коментар не відповів на власне питання, тому я додав, що як коментар, а не як відповідь. (Відповіді з високим рівнем голосування мають такі твердження, як "INNER JOIN - синтаксис ANSI" та "неявна приєднання синтаксису ANSI є старшим", що нічого не говорить, тому що обидва синтаксиси - це різні синтаксиси ANSI.)
Mikko Rantalainen

Відповіді:


710

INNER JOIN - синтаксис ANSI, який слід використовувати.

Зазвичай це вважається більш читабельним, особливо коли ви приєднуєтесь до багатьох таблиць.

Його також можна легко замінити, OUTER JOINколи виникає потреба.

WHEREСинтаксис орієнтований більше реляційна модель.

Результат двох таблиць JOINed - це декартовий добуток таблиць, до яких застосований фільтр, який вибирає лише ті рядки, що поєднують стовпці, що відповідають.

Це простіше бачити за допомогою WHEREсинтаксису.

Що стосується вашого прикладу, то в MySQL (і в цілому в SQL) ці два запити є синонімами.

Також зауважте, що у MySQL також є STRAIGHT_JOINпункт.

За допомогою цього пункту ви можете контролювати JOINпорядок: яка таблиця сканується у зовнішньому циклі, а яка - у внутрішній.

Ви не можете керувати цим в MySQL за допомогою WHEREсинтаксису.


10
Дякую, Квасной. У вас багато деталей у ваших ансах; чи справедливо сказати, що "так, ці запити еквівалентні, але ви повинні використовувати внутрішнє з'єднання, оскільки воно читабельніше і простіше змінювати"?
allyourcode

8
@allyourcode: для Oracle, SQL Server, MySQLі PostgreSQL- так. Для інших систем, мабуть, теж, але вам краще перевірити.
Quassnoi

13
FWIW, використання коми в умовах приєднання в WHEREпункті, також є стандартом ANSI.
Білл Карвін

1
@Bill Karwin: JOINключове слово не було частиною власних стандартів до останніх останніх, що може здатися. Він пробився Oracleлише у версію 9та PostgreSQLу версію 7.2(обидві випущені у 2001). Поява цього ключового слова була частиною ANSIстандартного прийняття, і саме тому це ключове слово зазвичай асоціюється ANSI, незважаючи на те, що останній також підтримує кому як синонім CROSS JOIN.
Quassnoi

9
Тим не менш, ANSI SQL-89 вказав, що з'єднання повинні бути виконані з комами та умовами в WHEREпункті (без умов, з'єднання еквівалентно перехресному з'єднанню, як ви вже говорили). ANSI SQL-92 додав JOINключове слово та відповідний синтаксис, але синтаксис у стилі кома все ще підтримується для зворотної сумісності.
Білл Карвін

182

Інші зазначають, що INNER JOINдопомагає читанню людини, і це головний пріоритет, я згоден.
Дозвольте спробувати пояснити, чому синтаксис приєднання є більш читабельним.

Основний SELECTзапит:

SELECT stuff
FROM tables
WHERE conditions

SELECTСтаття говорить нам , що ми отримуємо назад; FROMпункт говорить нам , де ми отримуємо його від, і WHEREпункт говорить нам , які з них ми отримуємо.

JOIN - це твердження про таблиці, про те, як вони пов'язані між собою (концептуально, власне, в одну таблицю).

Будь-які елементи запиту, які керують таблицями - звідки ми отримуємо речі - семантично належать до FROMпункту (і, звичайно, саме там JOINідуть елементи). Якщо ввести елементи приєднання до WHEREпункту, суперечить, що і звідки , тому JOINсинтаксис є кращим.


7
Дякуємо, що пояснили, чому саме Карл вважає за краще внутрішнє з'єднання. Я думаю, що твій анс мав на увазі інші, але явний звичайно кращий (так, я фанат Python).
allyourcode

2
Семантика ON і WHERE означає, що для JOINs після останнього OUTER JOIN не має значення, який ви використовуєте. Хоча ви характеризуєте ON як частину JOIN, це також фільтрування після декартового продукту. І ВКЛ, і ДЕ фільтрують декартовий продукт. Але або ON, або підвідділ WHERE повинен бути використаний до останнього ВИХІДНОГО ПРИЄДНАННЯ (ПРИЄДНАЙТЕСЬ не є "стовпчиками" пар стовпчиків. Будь-які дві таблиці можна приєднати до будь-якої умови. Це лише спосіб інтерпретувати рівність стовпців "ПРИЄДНАЙТЕСЬ НА ВІДКЛЮЧЕННЯ".
philipxy

Навіть коли ви використовуєте WHERE для того ж ефекту INNER JOIN, ви збираєтесь згадати свої дві таблиці у ВІДЧІЛІ частини запиту. Тому в основному ви все ще маючи на увазі , де ви отримуєте ваші дані в ЕК, так що я думаю , ви не можете сказати , що це обов'язково « це ототожнення який і де-с»
cybergeek654

@ArsenKhachaturyan Тільки тому, що в тексті використовується ключове слово або ідентифікатор, це не означає, що це код та формат коду. Це вибір форматування, який може піти будь-яким шляхом, і якщо це доречно редагувати тут, то виправдано, щоб кожна публікація постійно редагувалася в іншому форматі, - це означає, що це не виправдано. (Плюс вбудований формат коду на слово може бути важко прочитати.) Те саме, що і для абзаців, тут немає - вони не особливо уточнюються. Те саме з "що" проти "те". І назви мов програмування не повинні бути у форматі коду. PS Ви помилково додали розрив рядка.
філіпсі

@philipxy, як ви згадували, "це не означає ...", але, очевидно, ні це не означало, що його не можна позначати кодовим ключовим словом. Так, це вибір, який потрібно зробити, але багато постів робиться, не знаючи цього факту. Отже, моє рішення про внесення змін не спрямоване на те, щоб щось порушити, а зробити його більш зрозумілим. Якщо ви помітили будь-яку перерву після форматування змін, вибачте за це, і ви, очевидно, можете змінити такі зміни.
Арсен Хачатурян

143

Застосування умовних висловлювань у ON / WHERE

Тут я пояснив етапи обробки логічних запитів.


Довідка: Всередині Microsoft® SQL Server ™ 2005 Запит на T-SQL
Видавець: Microsoft Press
Pub Дата: 07 березня 2006 р.
Друк ISBN-10: 0-7356-2313-9
Друк ISBN-13: 978-0-7356-2313-2
Сторінки: 640

Всередині Microsoft® SQL Server ™ 2005 T-SQL Querying

(8)  SELECT (9) DISTINCT (11) TOP <top_specification> <select_list>
(1)  FROM <left_table>
(3)       <join_type> JOIN <right_table>
(2)       ON <join_condition>
(4)  WHERE <where_condition>
(5)  GROUP BY <group_by_list>
(6)  WITH {CUBE | ROLLUP}
(7)  HAVING <having_condition>
(10) ORDER BY <order_by_list>

Перший помітний аспект SQL, який відрізняється від інших мов програмування, - це порядок обробки коду. У більшості мов програмування код обробляється в тому порядку, в якому він написаний. У SQL перше, що обробляється, є пропозицією FROM, тоді як пункт SELECT, який з’являється першим, обробляється майже останнім.

Кожен крок генерує віртуальну таблицю, яка використовується як вхід до наступного кроку. Ці віртуальні таблиці недоступні абоненту (клієнтська програма або зовнішній запит). Лише таблиця, що генерується на останньому кроці, повертається абоненту. Якщо певний пункт не вказаний у запиті, відповідний крок просто пропускається.

Короткий опис етапів обробки логічного запиту

Не переживайте занадто сильно, якщо опис кроків наразі не має особливого сенсу. Вони надаються в якості довідки. Розділи, що з’являються після прикладу сценарію, будуть висвітлювати кроки набагато детальніше.

  1. ВІД: Декартовий продукт (перехресне з'єднання) виконується між першими двома таблицями пункту FROM, і в результаті створюється віртуальна таблиця VT1.

  2. ON: фільтр ON застосовується до VT1. У <join_condition>VT2 вставляються лише рядки, для яких значення TRUE.

  3. ВНУТРІШНЕ (приєднання): Якщо вказано ВІДНІШНЕ ПРИЄДНАННЯ (на відміну від CROSS JOIN або ВНУТРІШНЕ ПРИЄДНАННЯ), рядки із збереженої таблиці або таблиць, для яких не знайдено збігу, додаються до рядків VT2 як зовнішні рядки, генеруючи VT3 Якщо в пункті FROM з’являється більше двох таблиць, кроки 1 - 3 застосовуються повторно між результатом останнього з'єднання та наступною таблицею в пункті FROM, поки всі таблиці не будуть оброблені.

  4. ДЕ: Фільтр WHERE застосовується до VT3. У <where_condition>VT4 вставляються лише рядки, для яких значення TRUE.

  5. GROUP BY: Рядки з VT4 розташовані в групи на основі списку стовпців, зазначеного в пункті GROUP BY. VT5 генерується.

  6. КУБА | ROLLUP: Супергрупи (групи груп) додаються до рядків з VT5, генеруючи VT6.

  7. ВИДАЛЕННЯ: Фільтр HAVING застосовується до VT6. У <having_condition>VT7 вставляються лише групи, для яких значення TRUE.

  8. SELECT: Список SELECT обробляється, генеруючи VT8.

  9. DISTINCT: Дублікати рядків видаляються з VT8. VT9 генерується.

  10. ЗАМОВИТИ ЗА: Рядки з VT9 сортуються відповідно до списку стовпців, зазначеного у пункті ЗАМОВЛЕННЯ ПО. Створюється курсор (VC10).

  11. ВЕРХ: Вказане число або відсоток рядків вибирається з початку VC10. Таблиця VT11 генерується та повертається абоненту.



Тому (INNER JOIN) ON буде фільтрувати дані (кількість даних VT зменшиться тут само) перед тим, як застосувати пункт WHERE. Подальші умови приєднання будуть виконані з відфільтрованими даними, що покращує продуктивність. Після цього лише умови WHERE застосовуватимуть умови фільтра.

(Застосування умовних операторів у програмі ON / WHERE не матиме великої різниці в кількох випадках. Це залежить від того, скільки таблиць ви приєднали та кількість рядків, доступних у кожних таблицях приєднання)


10
"Тому (INNER JOIN) ON буде фільтрувати дані (кількість даних VT зменшиться тут само) перед застосуванням пункту WHERE." Не обов'язково. У статті йдеться про логічний порядок опрацювання. Коли ви говорите, що певна реалізація зробить одну справу перед іншою, ви говорите про реалізований порядок обробки. Реалізаціям дозволяється проводити будь-які оптимізації, які їм подобаються, якщо результат буде таким самим, як якщо б реалізація виконувалась логічним порядком. Джо Селко багато писав про це на Usenet.
Майк Шеррілл 'Відкликання котів'

@rafidheen "(INNER JOIN) ON буде фільтрувати дані ... перед тим, як застосувати пункт WHERE ... що покращує продуктивність." Гарна думка. "Після цього тільки умова WHERE застосовуватиметься умови фільтра". Що з пунктом HAVING?
Джеймс

@James Це твердження rafidheen неправильне. Див. "Приєднатися до оптимізації" в посібнику. Також мої інші коментарі на цій сторінці. (І MikeSherrill'CatRecall''s.) Такі "логічні" описи описують значення результату, а не те, як воно насправді обчислюється. І така поведінка впровадження не гарантовано не зміниться.
Філіпсі

67

Синтаксис ANSI з приєднанням старіший, менш очевидний і не рекомендується.

Крім того, реляційна алгебра дозволяє взаємозамінювати предикати в WHEREпункті та INNER JOIN, тому навіть INNER JOINзапити з WHEREпропозиціями можуть мати предикати, переставлені оптимізатором.

Рекомендую писати запити максимально читаним способом.

Іноді це включає створення INNER JOINвідносно "неповних" та внесення деяких критеріїв WHEREпросто для того, щоб зробити списки критеріїв фільтрації більш легкими у здійсненні.

Наприклад, замість:

SELECT *
FROM Customers c
INNER JOIN CustomerAccounts ca
    ON ca.CustomerID = c.CustomerID
    AND c.State = 'NY'
INNER JOIN Accounts a
    ON ca.AccountID = a.AccountID
    AND a.Status = 1

Написати:

SELECT *
FROM Customers c
INNER JOIN CustomerAccounts ca
    ON ca.CustomerID = c.CustomerID
INNER JOIN Accounts a
    ON ca.AccountID = a.AccountID
WHERE c.State = 'NY'
    AND a.Status = 1

Але це, звичайно, залежить.


16
Ваш перший фрагмент, безумовно, більше шкодить моєму мозку. Хтось насправді це робить? Якщо я зустріну когось, хто це робить, чи нормально мені бити його по голові?
allyourcode

3
Я розміщую критерії там, де це має найбільше сенс. Якщо я приєднаюся до тимчасово послідовної таблиці пошуку знімків (і у мене немає представлення даних або UDF, що примушує виділити дійсну дату), я включатиму ефективну дату в об'єднанні, а не в БУДЬ, тому що менше швидше за все, випадково його видалять.
Кейд Ру

14
@allyourcode: хоча цей тип синтаксису приєднання є рідким у INNER JOINs, це досить часто для RIGHT JOINs та LEFT JOINS - уточнення більш детальної інформації в предикаті приєднання виключає необхідність в підзапиті і запобігає ненавмисному перетворенню вашого зовнішнього з'єднання. до внутрішніх приєднань. (Хоча я згоден, що для INNER JOIN я майже завжди ставив c.State = 'NY' у пункті WHERE)
Дейв Маркл

1
@allyourcode Я точно це роблю! І я згоден з Кейд .. Я цікаво, є чи там пристойна причина не
Arth

31

Неявне приєднання (яке називається вашим першим запитом) стає набагато більш заплутаним, важким для читання та важким обслуговуванням, коли вам потрібно почати додавати більше запитів до запиту. Уявіть, що ви робите той самий запит і тип з'єднання на чотирьох-п'яти різних таблицях ... це кошмар.

Використання явного з'єднання (ваш другий приклад) набагато легше читати і легко обслуговувати.


48
Я більше не могла погодитися. ПРИЄДНАЙТЕСЬ синтаксис надзвичайно багатослівний і складно організувати. У мене є багато запитів, що об'єднують 5, 10, навіть 15 таблиць із використанням пункту WHERE, і вони чудово читаються. Переписування такого запиту за допомогою синтаксису JOIN призводить до неприємного безладу. Що просто показує, що на це питання немає правильної відповіді, і це більше залежить від того, що вам подобається.
Ной Ітер

33
Ное, я думаю, ти можеш бути тут меншиною.
matt b

2
Я отримую +1 до мату та Ноя. Мені подобається різноманітність :). Я бачу, звідки походить Ной; внутрішнє з'єднання не додає мови нічого нового, і, безумовно, більш багатослівне. З іншого боку, це може зробити ваш стан "куди" значно коротшим, що зазвичай означає, що його легше читати.
allyourcode

5
Я б припустив, що будь-яка розумна СУБД переведе два запити в один і той же план виконання; однак насправді кожна СУБД є різною, і єдиний спосіб точно знати - це насправді вивчити план виконання (тобто вам доведеться перевірити його самостійно).
мат b

Це правда, як @rafidheen запропонував в іншій відповіді (тієї, яка має детальну послідовність виконання SQL), що JOIN фільтруються по черзі, зменшуючи розмір операцій приєднання порівняно з повним декартовим з'єднанням з 3 або більше таблиць, з де фільтр застосовується заднім числом? Якщо це так, то було б запропоновано JOIN запропонувати покращення продуктивності (а також переваги в лівому / правому з'єднанні, як також вказувалося на іншій відповіді).
Джеймс

26

Я також зазначу, що використання старшого синтаксису більше піддається помилкам. Якщо ви використовуєте внутрішнє з'єднання без пункту ON, ви отримаєте синтаксичну помилку. Якщо ви використовуєте старіший синтаксис і забудете одне з умов приєднання в пункті де, ви отримаєте перехресне з'єднання. Розробники часто виправляють це, додаючи чітке ключове слово (замість того, щоб виправляти з'єднання, оскільки вони все ще не розуміють, що з'єднання порушено), що, можливо, виправить проблему, але значно сповільнить запит.

Крім того, для технічного обслуговування, якщо ви маєте перехресне з'єднання у старому синтаксисі, як утримувач буде знати, якщо ви мали намір мати його (є ситуації, коли потрібні перехресні з'єднання) або якщо це була аварія, яку слід виправити?

Дозвольте вказати на це запитання, щоб зрозуміти, чому неявний синтаксис поганий, якщо ви використовуєте ліві приєднання. Sybase * = до Ansi Standard з двома різними зовнішніми таблицями для тієї ж внутрішньої таблиці

Плюс до цього (персональний рент тут), стандарт, який використовує явні приєднання, старший за 20 років, а це означає, що синтаксис неявного приєднання застарів на ці 20 років. Ви б написали код програми за допомогою синтаксису, який застарів уже 20 років? Чому ви хочете написати такий код бази даних?


3
@HLGEM: Хоча я повністю погоджуюся з тим, що явні приєднання є кращими, є випадки, коли вам просто потрібно використовувати старий синтаксис. Приклад із реального світу: ANSI JOIN потрапив у Oracle лише у версії 9i, яка вийшла у 2001 році, і до цього лише року тому (16 років з моменту опублікування стандарту) мені довелося підтримати купу 8i інсталяції, для якої ми мали випустити критичні оновлення. Я не хотів підтримувати два набори оновлень, тому ми розробили та протестували оновлення для всіх баз даних, включаючи 8i, що означало, що ми не можемо використовувати ANSI JOIN.
Quassnoi

+1 цікавий момент, коли ви вказуєте, що sintax без INNER JOIN є більш схильним до помилок. Мене бентежить ваше останнє речення, коли ви говорите "... стандарт, який використовує явні приєднання, - 17 років". тож ви тоді пропонуєте використовувати ключове слово INNER JOIN чи ні?
Марко Демайо

1
@Marco Demaio, так, завжди використовуйте INNER JOIN або JOIN (ці два однакові) або LEFT JOIN, або ПРАВО ПРИЄДНАЙТЕСЬ, або CROSS JOIN, і ніколи не використовуй непрямих комах.
HLGEM

2
"Чому ви хочете написати код бази даних, який [20 років]?" - Я помічаю, що ви пишете SQL, використовуючи HAVINGякий "застарів", оскільки SQL почав підтримувати отримані таблиці. Я також зауважую, що ви не користуєтесь, NATURAL JOINхоча б я стверджував, що це стало INNER JOIN"застарілим". Так, у вас є свої причини (не потрібно їх знову вказувати тут!): Я можу сказати, що у тих, хто любить використовувати старіший синтаксис, є і свої причини, і відносний вік синтаксису є малозначимим.
одного дня, коли

1
ДЕ все ще в стандарті (покажіть мені, де його немає). Так, мабуть, нічого застарілого. Крім того, "замість виправлення з'єднання" показує мені розробника, якого слід тримати подалі від СУБД взагалі далеко .
Юрген А. Ерхард

12

Вони мають інше, читабельне для людини значення.

Однак, залежно від оптимізатора запитів, вони можуть мати однакове значення для машини.

Ви завжди повинні код читати.

Тобто, якщо це вбудовані відносини, використовуйте явне з'єднання. якщо ви збігаєтеся на слабко пов'язаних даних, використовуйте пункт де.


11

Стандарт SQL: 2003 змінив деякі правила пріоритетності, тому оператор JOIN має перевагу над об'єднанням "кома". Це фактично може змінити результати вашого запиту залежно від способу налаштування. Це спричиняє деякі проблеми для деяких людей, коли MySQL 5.0.12 перейшов на дотримання стандарту.

Отже, у вашому прикладі ваші запити працювали б однаково. Але якщо ви додали третю таблицю: ВИБІРТЕ ... З таблиці1, таблиця2 ПРИЄДНАЙТЕ у таблицю3 НА… ДЕ…

До MySQL 5.0.12 спочатку будуть приєднані table1 та table2, а потім table3. Тепер (5.0.12 і далі) спочатку приєднуються таблиця2 та таблиця3, потім таблиця1. Це не завжди змінює результати, але це може, і ви можете навіть не усвідомлювати цього.

Я ніколи більше не використовую синтаксис "кома", вибираючи ваш другий приклад. Так чи інакше, це зручніше для читання, умови ПРИЄДНАННЯ - з JOIN, а не розділені в окремий розділ запитів.


Стандартний SQL не змінився. MySQL був просто неправий і тепер правильно. Дивіться посібник з MySQL.
Філіпсі

4

Я знаю, що ви говорите про MySQL, але все одно: в Oracle 9 явні приєднання та неявні приєднання створюють різні плани виконання. AFAIK, який вирішено в Oracle 10+: такої різниці вже немає.


1

Синтаксис приєднання ANSI, безумовно, більш портативний.

Я переглядаю оновлення Microsoft SQL Server, і я також зазначу, що синтаксис = * і * = для зовнішніх з'єднань у SQL Server не підтримується (без режиму сумісності) для сервера 2005 sql та пізніших версій.


2
Навіть у SQL Server 2000, = і = можуть дати неправильні результати і ніколи не повинні використовуватися.
HLGEM

2
*=і =*ніколи не були ANSI і ніколи не були хорошими позначеннями. Ось чому ПО було потрібне - для ВНУТРІШНІХ ПРИЄДНАННЯ за відсутності підселектів (які додаються одночасно, тому вони фактично не потрібні в CROSS & INNER JOIN.)
philipxy

1

Якщо ви часто програмуєте динамічні збережені процедури, ви полюбите свій другий приклад (використовуючи де). Якщо у вас є різні вхідні параметри і багато мордочки, то це єдиний спосіб. В іншому випадку вони обидва будуть виконувати один і той же план запитів, тому однозначно різниці в класичних запитах точно немає.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.