SQL зліва приєднатися до декількох таблиць на лінії FROM?


256

Більшість діалектів SQL приймають обидва наступні запити:

SELECT a.foo, b.foo
FROM a, b
WHERE a.x = b.x

SELECT a.foo, b.foo
FROM a
LEFT JOIN b ON a.x = b.x

Тепер, очевидно, коли вам потрібно зовнішнє з'єднання, потрібен другий синтаксис. Але коли роблю внутрішнє з'єднання, чому я повинен віддавати перевагу другому синтаксису першому (або навпаки)?


1
Гуффа: Як ти це знайшов? Хоча моє питання є кращою практикою, ніж "як мені"
jmucchiello

Оскільки це найкраща практика, будь ласка, зробіть це Wiki.
Біной Антоній

1
Я не думаю, що хтось коментував виконання цих двох. Чи може хтось підтвердити чи навести щось розумне стосовно будь-яких суттєвих відмінностей?
ahnbizcad

@ahnbizcad Два задані запити не роблять одне і те ж. Перший повертає те саме, що і INNER JOIN ON. Реалізація залежить від версії СУБД, і навіть тоді вона має невеликі гарантії. Але перетворення СУБД, що еквівалентні випадки кома проти ВНУТРІШНЬОГО ПРИЄДНАННЯ ВІД / ГДЕ проти КРЕСТОГО ПРИЄДНУЙТЕСЯ, де ТРИ дрібницею. Дізнайтеся про оптимізацію / реалізацію запитів реляційних баз даних.
філіпсі

отримали рекомендацію щодо ресурсів? гігантські, щільні посібники, тому я намагаюся вчитися звідси.
ahnbizcad

Відповіді:


319

У WHEREбільшості сучасних баз даних застарілий синтаксис із простою переліком таблиць та використанням пункту для визначення критеріїв приєднання застарілий.

Це не лише для показу, старий синтаксис має можливість бути неоднозначним, коли ви використовуєте як INNER, так і OUTER приєднується в одному запиті.

Дозвольте навести вам приклад.

Припустимо, у вашій системі є 3 таблиці:

Company
Department
Employee

Кожна таблиця містить численні рядки, пов’язані між собою. У вас є кілька компаній, і кожна компанія може мати декілька відділів, і кожен відділ може мати декількох працівників.

Гаразд, тепер вам потрібно зробити наступне:

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

Отже, ви робите це:

SELECT * -- for simplicity
FROM Company, Department, Employee
WHERE Company.ID *= Department.CompanyID
  AND Department.ID = Employee.DepartmentID

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

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

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

Причиною цього є те, що WHEREпункт визначає, які рядки закінчуються в кінцевому результаті, а не окремі частини рядків.

І в цьому випадку, завдяки лівому об'єднанню, стовпець Department.ID буде NULL, і, коли мова заходить про INNER JOIN для Співробітника, немає можливості виконати це обмеження для ряду Співробітник, і тому воно не буде з'являються.

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

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

Введіть новий синтаксис, за допомогою якого ви можете вибрати.

Наприклад, якщо ви хочете, щоб усі компанії, як зазначено в описі проблеми, писали:

SELECT *
FROM Company
     LEFT JOIN (
         Department INNER JOIN Employee ON Department.ID = Employee.DepartmentID
     ) ON Company.ID = Department.CompanyID

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

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

SELECT *
FROM Company
     LEFT JOIN (
         Department INNER JOIN Employee ON Department.ID = Employee.DepartmentID
     ) ON Company.ID = Department.CompanyID AND Department.Name LIKE '%X%'

Цей додатковий пункт використовується для приєднання, але не є фільтром для всього рядка. Таким чином, рядок може з’являтися з інформацією про компанію, але може містити NULL у всіх стовпцях департаменту та службовців для цього рядка, оскільки для цієї компанії не існує відділу, у якому є ім'я X. Це важко зі старим синтаксисом.

Ось чому, серед інших постачальників, Microsoft знищила старий синтаксис зовнішнього з'єднання, але не старий внутрішній синтаксис приєднання, починаючи з SQL Server 2005 і вище. Єдиний спосіб спілкуватися з базою даних, що працює на Microsoft SQL Server 2005 або 2008, використовуючи старий синтаксис зовнішнього приєднання, це встановити цю базу даних у режимі сумісності 8.0 (він же SQL Server 2000).

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

Так ось у вас це є.

ЛІВ І ВНУТРІШНЯ ПРИЄДНАЙТЕСЬ - це хвиля майбутнього.


28
"застаріла в більшості сучасних баз даних." --- просто цікаво, які з них?
zerkms

10
пробачте, я не знайомий з оператором * =, що він робить? Дякую!
ultrajohn

9
Зірка = і = Зірка (добре були) праворуч і ліворуч приєднуються, або це ліво-праворуч? Застарілі протягом століть, я не використовував їх з SQL Server 6.
Тоні Хопкінсон,

3
Кома не застаріла. Ніколи нестандартний OUTER JOINсинтаксис *=// =*/ *=*застарілий.
philipxy

1
Ця відповідь навіть не відповідає на питання, яке не стосується зовнішніх приєднань. Одне твердження, яке вона робить про кому проти ВНУТРІШНЬОГО ВІДКЛЮЧЕННЯ, повторна оптимізація, помилкове.
філіпсі

17

Синтаксис JOIN зберігає умови біля таблиці, до якої вони застосовуються. Це особливо корисно, коли ви приєднуєтесь до великої кількості таблиць.

До речі, ви можете зробити зовнішнє з'єднання і з першим синтаксисом:

WHERE a.x = b.x(+)

Або

WHERE a.x *= b.x

Або

WHERE a.x = b.x or a.x not in (select x from b)

2
Синтаксис * = застарілий у MS SQLServer і з поважних причин: Мало того, що це ускладнює читання, але він не робить те, що люди думають, що це робить, і НЕ є таким же, як схоже виглядає ЛІВНЕ ПРИЄДНАЙТЕСЬ. Синтаксис (+) мені незнайомий; яка реалізація SQL робить це?
Євро Міцеллі

2
Інший синтаксис використовується принаймні Oracle.
Лассе В. Карлсен

4
Ніколи не використовуйте синтаксис SQL Server * =, він НЕ дасть послідовних результатів, оскільки інколи трактуватиме як перехресне з'єднання, а не ліве з'єднання. Це стосується навіть SQL Server 2000. Якщо у вас є який-небудь код, використовуючи це, вам потрібно виправити.
HLGEM

12

Перший спосіб - старший стандарт. Другий метод був введений в SQL-92, http://en.wikipedia.org/wiki/SQL . Повний стандарт можна переглянути за посиланням http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt .

Минуло багато років, перш ніж компанії-бази даних прийняли стандарт SQL-92.

Тож причиною, чому другий метод є кращим, є стандарт SQL згідно з комітетом ANSI та ISO.


,як і раніше стандарт. onпотрібно було запровадити лише для того, outer joinяк були введені також підселектори.
філіпсі

12

В основному, коли у вашому пункті ВІД перелічено такі таблиці:

SELECT * FROM
  tableA, tableB, tableC

результат - перехресний добуток усіх рядків у таблицях A, B, C. Тоді ви застосовуєте обмеження, WHERE tableA.id = tableB.a_idяке викине величезну кількість рядків, потім далі ... AND tableB.id = tableC.b_idі вам слід отримати лише ті рядки, які вас справді цікавлять в.

СУБД знають, як оптимізувати цей SQL, щоб різниця в продуктивності написання цього методу за допомогою JOIN була незначною (якщо така є). Використання нотації JOIN робить оператор SQL більш читабельним (IMHO, не використовуючи приєднання, перетворює заяву в безлад). Використовуючи перехресний продукт, потрібно вказати критерії приєднання в пункті WHERE, і це проблема в позначенні. Ви переповнюєте свій пункт WHERE подібними речами

    tableA.id = tableB.a_id 
AND tableB.id = tableC.b_id 

який використовується лише для обмеження поперечного продукту. ДЕРЖАВНИЙ ЗАГАЛЬНИЙ ЗАЛІЗ ДІЙ повинен містити лише ОБМЕЖЕННЯ до набору результатів. Якщо ви змішуєте критерії приєднання таблиці з обмеженнями набору результатів, вам (та іншим) ваш запит буде важче прочитати. Ви обов'язково повинні використовувати JOINs та зберегти FROM пункт FROM, а WHERE - WHERE.


10

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

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

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


6

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

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


5

SELECT * FROM table1, table2, ...Синтаксис нормально в протягом декількох таблиць, але вона стає експоненціально ( не обов'язково математично точне твердження ) все важче і важче читати , як кількість таблиць збільшується.

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

Крім того, якщо всі з'єднання є INNER, то обидві версії є рівнозначними. Однак, коли у вас є ВИКОРИСТЬНИЙ приєднатися до будь-якої частини заяви, речі стають набагато складнішими, і це практично гарантія того, що те, що ви пишете, не буде запитувати те, що ви думаєте, що написали.


2

Коли вам потрібно зовнішнє з'єднання, не завжди потрібен другий синтаксис :

Oracle:

SELECT a.foo, b.foo
  FROM a, b
 WHERE a.x = b.x(+)

MSSQLServer (хоча він був застарілим у 2000 році) / Sybase:

SELECT a.foo, b.foo
  FROM a, b
 WHERE a.x *= b.x

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


Сервер SQL заборонив цей синтаксис лівого приєднання, і навіть у SQL Server 2000 він не буде послідовно давати правильні результати (іноді він робить перехресне з'єднання замість лівого з'єднання) і ніколи не повинен використовуватися в SQL Server.
HLGEM

@HLGEM: Дякую за інформацію. Я збираюся ОНОВЛИТИ свій пост, щоб відобразити те, що ви говорите.
Пабло Санта-Крус

0

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


1
У мене виховувалася звичка не використовувати синтаксис JOIN і робити це першим способом. Я мушу визнати, що я все ще часто
зациклювався на звичаях

3
Мене так і вчили. Я змінив свій стиль кодування, тому що люди дивляться на це і не легко розпізнають, що відбувається. Оскільки немає логічної різниці, і я не можу знайти причин для вибору першого за останній, я відчув, що мені слід адаптуватися до того, щоб зробити код більш зрозумілим, щоб допомогти іншим зрозуміти, що я пишу.
kemiller2002

0

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


0

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

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