Що таке SQL JOIN
і що це різні типи?
Що таке SQL JOIN
і що це різні типи?
Відповіді:
SQL JOIN
?SQL JOIN
це спосіб отримання даних з двох або більше таблиць баз даних.
SQL JOIN
s?Всього їх п’ять JOIN
. Вони є :
1. JOIN or INNER JOIN
2. OUTER JOIN
2.1 LEFT OUTER JOIN or LEFT JOIN
2.2 RIGHT OUTER JOIN or RIGHT JOIN
2.3 FULL OUTER JOIN or FULL JOIN
3. NATURAL JOIN
4. CROSS JOIN
5. SELF JOIN
У цьому виді а JOIN
ми отримуємо всі записи, які відповідають умові в обох таблицях, а записи в обох таблицях, які не відповідають, не повідомляються.
Іншими словами, INNER JOIN
заснований на єдиному факті, що: ТОЛЬКО повинні бути вказані ТОЛЬКІ відповідні записи в ТАКІ.
Зверніть увагу , що JOIN
без будь - яких інших JOIN
ключових слів (наприклад INNER
, OUTER
, LEFT
і т.д.) є INNER JOIN
. Іншими словами, JOIN
це синтаксичний цукор для INNER JOIN
(див. Різниця між ПРИЄДНАЙТЕСЬ та ВНУТРІШНЬОЮ ПРИЄДНАЙТЕСЬ ).
OUTER JOIN
витягує
Або відповідні рядки з однієї таблиці, і всі рядки в іншій таблиці. Або всі рядки в усіх таблицях (не має значення, чи є відповідність чи ні).
Існує три види зовнішнього приєднання:
2.1 ПРИЄДНАЙТЕСЬ ЛІВО ЗОВНІШНЕ або ПРИЄДНАЙТЕСЬ ЛІВО
Це з'єднання повертає всі рядки з лівої таблиці спільно з відповідними рядками з правої таблиці. Якщо у правій таблиці немає стовпців, вони повертають NULL
значення.
2.2 ПРАВИЛЬНЕ ПРИЄДНУЙСЯ ВНУТРІШНО або ПРАВО ПРИЄДНАЙТЕ
Це JOIN
повертає всі рядки з правої таблиці спільно з відповідними рядками з лівої таблиці. Якщо в лівій таблиці немає стовпців, вони повертають NULL
значення.
2.3 ПОВНІШНІЙ ПРИЄДНАЙТЕСЬ або ПОВНОГО ПРИЄДНАННЯ
Це JOIN
поєднує LEFT OUTER JOIN
і RIGHT OUTER JOIN
. Він повертає рядки з будь-якої таблиці, коли умови виконуються, і повертає NULL
значення, коли немає відповідності.
Іншими словами, OUTER JOIN
це засновано на тому, що: ТОЛЬКО повинні бути перераховані відповідні записи в ОДНІЙ з таблиць (ВПРАВО або ВЛІВО) або ВІД СТОЛІТЬ таблиць (ПОВНО).
Note that `OUTER JOIN` is a loosened form of `INNER JOIN`.
Він заснований на двох умовах:
JOIN
робиться на всіх стовпцях з однаковою назвою для рівності.Це, здається, більше теоретичного характеру, і в результаті (мабуть) більшість СУБД навіть не намагаються це підтримати.
Це декартовий продукт двох задіяних таблиць. Результат CROSS JOIN
заповіту не матиме сенсу в більшості ситуацій. Більше того, нам це взагалі не знадобиться (або потрібно найменше, якщо бути точним).
Це не інша форма JOIN
, а скоріше це JOIN
( INNER
, OUTER
і т.д.) таблиці для себе.
Залежно від оператора, який використовується для JOIN
пункту, може бути два типи JOIN
s. Вони є
Для будь-якого JOIN
типу ( INNER
, OUTER
і т. Д.), Якщо ми використовуємо ТІЛЬКИ оператор рівності (=), то ми говоримо, що JOIN
є an EQUI JOIN
.
Це те саме, EQUI JOIN
але це дозволяє всім іншим операторам, таких як>, <,> = і т.д.
Багато хто вважає , що
EQUI JOIN
і ThetaJOIN
аналогічніINNER
, іOUTER
т.д.JOIN
s. Але я переконаний, що це помилка і робить ідеї розпливчастими. Тому щоINNER JOIN
іOUTER JOIN
т. Д. Всі пов'язані з таблицями та їхніми даними, тоді якEQUI JOIN
іTHETA JOIN
пов'язані лише з операторами, якими ми користуємося в першій.Знову ж таки, багато хто вважає їх
NATURAL JOIN
якось "своєрідним"EQUI JOIN
. Насправді це правда, через першу умову, про яку я згадавNATURAL JOIN
. Однак нам не доведеться обмежувати це лишеNATURAL JOIN
одними.INNER JOIN
s,OUTER JOIN
s тощо може бутиEQUI JOIN
теж.
Визначення:
ПРИЄДНАЄТЬСЯ - це спосіб запиту даних, які поєднуються разом з декількох таблиць одночасно.
Що стосується RDBMS, існує 5 типів приєднань:
Equi-Join: Поєднує загальні записи з двох таблиць на основі умови рівності. Технічно об'єднання, виконане за допомогою оператора рівності (=) для порівняння значень первинного ключа однієї таблиці та значень зовнішнього ключа іншої таблиці, отже, набір результатів включає загальні (зіставлені) записи з обох таблиць. Про впровадження див. INNER-JOIN.
Natural-Join: Це вдосконалена версія Equi-Join, в якій операція SELECT опускає повторюваний стовпець. Про впровадження див. INNER-JOIN
Non-Equi-Join: Це зворотне значення Equi-join, коли умовою приєднання є використання, яке не є рівним оператором (=), наприклад,! =, <=,> =,>, <Або МЕЖДОМ тощо. Про реалізацію див. INNER-JOIN.
Self-Join:: індивідуальна поведінка приєднання, де таблиця поєднується з самим собою; Зазвичай це потрібно для запитів таблиць, що самостійно посилаються (або сутності Unar відносин). Про впровадження див. INNER-JOIN.
Декартовий продукт: він поєднує всі записи обох таблиць без будь-яких умов. Технічно він повертає набір результатів запиту без WHERE-пункту.
Відповідно до проблеми та прогресу SQL, існують 3 типи з'єднань, і всі з'єднання RDBMS можна досягти, використовуючи ці типи з'єднань.
ВНУТРІШНЯ ПРИЄДНАННЯ: Він об'єднує (або поєднує) збігані рядки з двох таблиць. Узгодження проводиться на основі загальних стовпців таблиць та їх порівняльної роботи. Якщо умова заснована на рівності, тоді: EQUI-JOIN виконується, інакше Non-EQUI-Join.
OUTER-JOIN: Він об'єднує (або поєднує) збігані рядки з двох таблиць і незрівняних рядків із значеннями NULL. Однак можна налаштувати підбір невідповідних рядків, наприклад, вибір не збігається рядка з першої таблиці або другої таблиці за підтипами: ВЛІТТЯ ВІДПРИЄМСТВО ПРИЄДНАЙТЕСЬ та ПРАВИЙ ЗАРІБ.
2.1. ЛІВНІ ВНУТРІШНІ ПРИЄДНАННЯ (він же, НАЛІГЛО-ПРИЄДНАЙТЕСЬ): Повертає відповідні рядки з двох таблиць і не збігаються з таблицею ВЛЕВО (тобто, перша таблиця).
2.2. ВПРАВИ ВЗУХОДЖЕННЯ ПРИЄДНАЙТЕСЬ (він же, ПРАВО-ПРИЄДНАЙТЕСЬ): Повертає відповідні рядки з двох таблиць і не збігаються лише з таблиці ПРАВО.
2.3. ПОВНІШНІЙ ПРИЄДНАЙТЕСЬ (інакше ЗОВНІШНІ ПРИЄДНАННЯ): Повертає зіставлені та не збігаються з обох таблиць.
CROSS-JOIN: Це з'єднання не зливається / комбінується, натомість виконує декартовий продукт.
Примітка. Самостійне приєднання може бути досягнуто як INNER-JOIN, OUTER-JOIN та CROSS-JOIN на основі вимог, але таблиця повинна приєднуватися сама.
Для отримання додаткової інформації:
1.1: ВНУТРІШНЯ ПРИЄДНАННЯ: Реалізація Equi-Join
SELECT *
FROM Table1 A
INNER JOIN Table2 B ON A.<Primary-Key> =B.<Foreign-Key>;
1.2: ВНУТРІШНЯ ПРИЄДНАННЯ: Реалізація Natural-JOIN
Select A.*, B.Col1, B.Col2 --But no B.ForeignKeyColumn in Select
FROM Table1 A
INNER JOIN Table2 B On A.Pk = B.Fk;
1.3: INNER-JOIN з реалізацією NON-Equi
Select *
FROM Table1 A INNER JOIN Table2 B On A.Pk <= B.Fk;
1.4: ВНУТРІШНЯ ПРИЄДНАЙТЕСЬ із САМОСТІЙНОЮ ПРИЄДНАЙТЕ
Select *
FROM Table1 A1 INNER JOIN Table1 A2 On A1.Pk = A2.Fk;
2.1: ЗОВНІШНЕ ПРИЄДНАННЯ (повне зовнішнє з'єднання)
Select *
FROM Table1 A FULL OUTER JOIN Table2 B On A.Pk = B.Fk;
2.2: ПРИЄДНАЙТЕСЬ ЛІВО
Select *
FROM Table1 A LEFT OUTER JOIN Table2 B On A.Pk = B.Fk;
2.3: ПРАВО ПРИЄДНАЙТЕСЬ
Select *
FROM Table1 A RIGHT OUTER JOIN Table2 B On A.Pk = B.Fk;
3.1: КРОВИЙ ПРИЄДНАЙТЕСЬ
Select *
FROM TableA CROSS JOIN TableB;
3.2: CROSS JOIN-Self JOIN
Select *
FROM Table1 A1 CROSS JOIN Table1 A2;
// АБО //
Select *
FROM Table1 A1,Table1 A2;
intersect
// except
/ union
; тут кола - це рядки, повернені left
& right
join
, як говорять пронумеровані мітки. Зображення AXB - це нісенітниця. cross join
= inner join on 1=1
& - особливий випадок першої діаграми.
UNION JOIN
. Зараз застаріла в SQL: 2003.
Цікаво, що більшість інших відповідей страждають від цих двох проблем:
Нещодавно я написав статтю на тему: Напевно, неповний, всебічний посібник з багатьох різних способів приєднатися до таблиць у SQL , який я підсумую тут.
Ось чому діаграми Венна пояснюють їх настільки неточно, оскільки ПРИЄДНАННЯ створює декартовий продукт між двома об'єднаними таблицями. Вікіпедія це прекрасно ілюструє:
Синтаксис SQL для декартових продуктів є CROSS JOIN
. Наприклад:
SELECT *
-- This just generates all the days in January 2017
FROM generate_series(
'2017-01-01'::TIMESTAMP,
'2017-01-01'::TIMESTAMP + INTERVAL '1 month -1 day',
INTERVAL '1 day'
) AS days(day)
-- Here, we're combining all days with all departments
CROSS JOIN departments
Який поєднує всі рядки з однієї таблиці та всі рядки з другої таблиці:
Джерело:
+--------+ +------------+
| day | | department |
+--------+ +------------+
| Jan 01 | | Dept 1 |
| Jan 02 | | Dept 2 |
| ... | | Dept 3 |
| Jan 30 | +------------+
| Jan 31 |
+--------+
Результат:
+--------+------------+
| day | department |
+--------+------------+
| Jan 01 | Dept 1 |
| Jan 01 | Dept 2 |
| Jan 01 | Dept 3 |
| Jan 02 | Dept 1 |
| Jan 02 | Dept 2 |
| Jan 02 | Dept 3 |
| ... | ... |
| Jan 31 | Dept 1 |
| Jan 31 | Dept 2 |
| Jan 31 | Dept 3 |
+--------+------------+
Якщо ми просто запишемо список розділених комами таблиць, отримаємо те саме:
-- CROSS JOINing two tables:
SELECT * FROM table1, table2
А INNER JOIN
- просто відфільтрований, CROSS JOIN
де Theta
в реляційній алгебрі називається предикат фільтра .
Наприклад:
SELECT *
-- Same as before
FROM generate_series(
'2017-01-01'::TIMESTAMP,
'2017-01-01'::TIMESTAMP + INTERVAL '1 month -1 day',
INTERVAL '1 day'
) AS days(day)
-- Now, exclude all days/departments combinations for
-- days before the department was created
JOIN departments AS d ON day >= d.created_at
Зауважте, що ключове слово INNER
необов’язкове (за винятком MS Access).
( дивіться приклади результатів у статті )
Особливим видом Theta-JOIN є джойні, які ми використовуємо найбільше. Присудок з'єднує первинний ключ однієї таблиці із зовнішнім ключем іншої таблиці. Якщо ми використовуємо базу даних Sakila для ілюстрації, ми можемо написати:
SELECT *
FROM actor AS a
JOIN film_actor AS fa ON a.actor_id = fa.actor_id
JOIN film AS f ON f.film_id = fa.film_id
Це поєднує всіх акторів зі своїми фільмами.
Або також на деяких базах даних:
SELECT *
FROM actor
JOIN film_actor USING (actor_id)
JOIN film USING (film_id)
USING()
Синтаксис дозволяє вказати стовпець , який повинен бути присутнім на кожній стороні JOIN таблиці операції і створює предикат рівності цих двох стовпців.
Інші відповіді вказали цей тип "ПРИЄДНАЙТЕСЬ" окремо, але це не має сенсу. Це просто синтаксична форма цукру для equi ПРИЄДНАЙТЕСЬ, що є особливим випадком Theta-JOIN або INNER JOIN. NATURAL JOIN просто збирає всі стовпці, які є загальними для обох таблиць, що об'єднуються, і приєднує USING()
ці стовпці. Що навряд чи корисне через випадкові збіги (як LAST_UPDATE
колонки в базі даних Sakila ).
Ось синтаксис:
SELECT *
FROM actor
NATURAL JOIN film_actor
NATURAL JOIN film
Тепер OUTER JOIN
трохи відрізняється від INNER JOIN
створення UNION
декількох декартових продуктів. Ми можемо написати:
-- Convenient syntax:
SELECT *
FROM a LEFT JOIN b ON <predicate>
-- Cumbersome, equivalent syntax:
SELECT a.*, b.*
FROM a JOIN b ON <predicate>
UNION ALL
SELECT a.*, NULL, NULL, ..., NULL
FROM a
WHERE NOT EXISTS (
SELECT * FROM b WHERE <predicate>
)
Ніхто не хоче писати останнього, тому ми пишемо OUTER JOIN
(що зазвичай краще оптимізується базами даних).
Можливо INNER
, тут ключове слово OUTER
необов’язкове.
OUTER JOIN
випускається у трьох ароматах:
LEFT [ OUTER ] JOIN
: Ліва таблиця JOIN
виразу додається до об'єднання, як показано вище.RIGHT [ OUTER ] JOIN
: Права таблиця JOIN
виразу додається до об'єднання, як показано вище.FULL [ OUTER ] JOIN
: Обидві таблиці JOIN
виразу додаються до об'єднання, як показано вище.Все це можна поєднувати з ключовим словом USING()
або з NATURAL
( нещодавно у мене був справжній випадок використанняNATURAL FULL JOIN
)
У Oracle та SQL Server є деякі історичні, застарілі синтаксиси, які підтримували OUTER JOIN
вже до того, як стандарт SQL мав для цього синтаксис:
-- Oracle
SELECT *
FROM actor a, film_actor fa, film f
WHERE a.actor_id = fa.actor_id(+)
AND fa.film_id = f.film_id(+)
-- SQL Server
SELECT *
FROM actor a, film_actor fa, film f
WHERE a.actor_id *= fa.actor_id
AND fa.film_id *= f.film_id
Сказавши це, не використовуйте цей синтаксис. Я просто перелічу це тут, щоб ви могли розпізнати це зі старих публікацій блогу / застарілого коду.
OUTER JOIN
Мало хто це знає, але стандарт SQL визначає розділений OUTER JOIN
(і Oracle реалізує це). Ви можете написати такі речі:
WITH
-- Using CONNECT BY to generate all dates in January
days(day) AS (
SELECT DATE '2017-01-01' + LEVEL - 1
FROM dual
CONNECT BY LEVEL <= 31
),
-- Our departments
departments(department, created_at) AS (
SELECT 'Dept 1', DATE '2017-01-10' FROM dual UNION ALL
SELECT 'Dept 2', DATE '2017-01-11' FROM dual UNION ALL
SELECT 'Dept 3', DATE '2017-01-12' FROM dual UNION ALL
SELECT 'Dept 4', DATE '2017-04-01' FROM dual UNION ALL
SELECT 'Dept 5', DATE '2017-04-02' FROM dual
)
SELECT *
FROM days
LEFT JOIN departments
PARTITION BY (department) -- This is where the magic happens
ON day >= created_at
Частини результату:
+--------+------------+------------+
| day | department | created_at |
+--------+------------+------------+
| Jan 01 | Dept 1 | | -- Didn't match, but still get row
| Jan 02 | Dept 1 | | -- Didn't match, but still get row
| ... | Dept 1 | | -- Didn't match, but still get row
| Jan 09 | Dept 1 | | -- Didn't match, but still get row
| Jan 10 | Dept 1 | Jan 10 | -- Matches, so get join result
| Jan 11 | Dept 1 | Jan 10 | -- Matches, so get join result
| Jan 12 | Dept 1 | Jan 10 | -- Matches, so get join result
| ... | Dept 1 | Jan 10 | -- Matches, so get join result
| Jan 31 | Dept 1 | Jan 10 | -- Matches, so get join result
Сенс у тому, що всі рядки з розділеної сторони з'єднання закінчуються результатом, незалежно від того, чи JOIN
відповідає щось на "іншій стороні СПОЛУЧЕННЯ". Довга коротка історія. Це для заповнення рідких даних у звітах. Дуже корисний!
Серйозно? Жодної іншої відповіді це не отримало? Звичайно, ні, тому що він не має нативного синтаксису в SQL, на жаль (як ANTI JOIN нижче). Але ми можемо використати IN()
і EXISTS()
, наприклад, знайти всіх акторів, які зіграли у фільмах:
SELECT *
FROM actor a
WHERE EXISTS (
SELECT * FROM film_actor fa
WHERE a.actor_id = fa.actor_id
)
WHERE a.actor_id = fa.actor_id
Предикат діє як стать предикатів. Якщо ви не вірите, перегляньте плани виконання, наприклад, в Oracle. Ви побачите, що база даних виконує операцію SEMI JOIN, а не EXISTS()
предикат.
Це як раз зворотне SEMI JOIN ( будьте обережні , щоб не використовувати , NOT IN
хоча , як це має важливе застереження)
Ось усі актори без фільмів:
SELECT *
FROM actor a
WHERE NOT EXISTS (
SELECT * FROM film_actor fa
WHERE a.actor_id = fa.actor_id
)
Деякі люди (особливо люди MySQL) також пишуть АНТИ ПРИЄДНАЙТЕ так:
SELECT *
FROM actor a
LEFT JOIN film_actor fa
USING (actor_id)
WHERE film_id IS NULL
Я думаю, що історичною причиною є продуктивність.
OMG, це занадто круто. Я єдиний, хто це згадує? Ось класний запит:
SELECT a.first_name, a.last_name, f.*
FROM actor AS a
LEFT OUTER JOIN LATERAL (
SELECT f.title, SUM(amount) AS revenue
FROM film AS f
JOIN film_actor AS fa USING (film_id)
JOIN inventory AS i USING (film_id)
JOIN rental AS r USING (inventory_id)
JOIN payment AS p USING (rental_id)
WHERE fa.actor_id = a.actor_id -- JOIN predicate with the outer query!
GROUP BY f.film_id
ORDER BY revenue DESC
LIMIT 5
) AS f
ON true
Він знайде ТОП-5 прибутків фільмів на одного актора. Щоразу, коли вам знадобиться запит TOP-N-на-щось, LATERAL JOIN
ви будете своїм другом. Якщо ви людина SQL Server, то ви знаєте цей JOIN
тип під назвоюAPPLY
SELECT a.first_name, a.last_name, f.*
FROM actor AS a
OUTER APPLY (
SELECT f.title, SUM(amount) AS revenue
FROM film AS f
JOIN film_actor AS fa ON f.film_id = fa.film_id
JOIN inventory AS i ON f.film_id = i.film_id
JOIN rental AS r ON i.inventory_id = r.inventory_id
JOIN payment AS p ON r.rental_id = p.rental_id
WHERE fa.actor_id = a.actor_id -- JOIN predicate with the outer query!
GROUP BY f.film_id
ORDER BY revenue DESC
LIMIT 5
) AS f
Добре, можливо, це обман, тому що a LATERAL JOIN
або APPLY
вираз - це "корельований підпит", який створює кілька рядків. Але якщо ми допускаємо "співвіднесені підзапити", ми також можемо говорити про ...
Це реально реалізовано лише Oracle та Informix (наскільки мені відомо), але це може бути емуляція в PostgreSQL за допомогою масивів та / або XML та в SQL Server за допомогою XML.
MULTISET
створює співвіднесений підзапит та вкладає отриманий набір рядків у зовнішній запит. Наведений нижче запит вибирає всіх акторів, і кожен актор збирає свої фільми в вкладену колекцію:
SELECT a.*, MULTISET (
SELECT f.*
FROM film AS f
JOIN film_actor AS fa USING (film_id)
WHERE a.actor_id = fa.actor_id
) AS films
FROM actor
Як ви вже бачили, є кілька типів JOIN , ніж просто «нудно» INNER
, OUTER
і CROSS JOIN
що, як правило , згадуються. Детальніше в моїй статті . І будь ласка, перестаньте використовувати схеми Венна для їх ілюстрації.
Я збираюся підштовхнути свого домашнього улюбленця: ключове слово USING.
Якщо обидві таблиці з обох сторін JOIN мають свої іноземні ключі, правильно названі (тобто, те саме ім'я, а не лише "id"), це можна використовувати:
SELECT ...
FROM customers JOIN orders USING (customer_id)
Я вважаю це дуже практичним, читабельним і не використовується досить часто.