SQL JOIN и различные типы соединений

что такое SQL JOIN а какие бывают разные типы?

7 ответов


иллюстрация из W3schools:


INNER JOIN - Only records which match the condition in both tables


LEFT JOIN - All records from table 1 in conjunction with records which match the condition in table 2


RIGHT JOIN - All records from table 2 in conjunction with records from table 1 which match the condition


FULL OUTER JOIN - Combination of both Left and Right Outer joins matching ON clause but preserving both tables



что это SQL JOIN ?

SQL JOIN - это метод извлечения данных из двух или более таблиц базы данных.

какие SQL JOINs ?

всего их пять JOINs. Они:

  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

1. Присоединиться или внутреннее соединение:

в таком виде JOIN мы получаем все записи, которые соответствуют условию в обеих таблицах, и записи в обеих таблицах, которые не соответствуют не сообщается.

в других слова, INNER JOIN основано на одном факте: должны быть перечислены только совпадающие записи в обеих таблицах.

обратите внимание, что a JOIN без каких-либо других JOIN ключевые слова (например,INNER, OUTER, LEFT и т. д.) - Это INNER JOIN. Другими словами,JOIN is синтаксический сахар для INNER JOIN (см.: разница между JOIN и INNER JOIN).

2. ВНЕШНЕЕ СОЕДИНЕНИЕ:

OUTER JOIN получает

либо, совпадающая строка из одной таблицы и всех строк в другой таблице Или, все строки во всех таблицах (неважно, есть ли совпадение или нет).

существует три вида внешнего соединения :

2.1 левое внешнее соединение или левое соединение

этот Join возвращает все строки из левой таблицы в сочетании с соответствующими строками из правый столик. Если в правой таблице нет совпадающих столбцов, она возвращает 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`.

3. ЕСТЕСТВЕННОЕ СОЕДИНЕНИЕ:

он основан на двух условиях:

  1. на JOIN производится на всех столбцах с одинаковым именем для равенства.
  2. удаляет повторяющиеся столбцы результат.

это кажется более теоретическим по своей природе и в результате (вероятно) большинство СУБД даже не утруждайте себя поддержкой.

4. CROSS JOIN:

это декартово произведение двух таблиц. Результат a CROSS JOIN не будет иметь смысла в большинстве ситуаций. Более того, нам это вообще не понадобится (или, если быть точным, нужно меньше всего).

5. SELF JOIN:

это не другая форма JOIN, а это JOIN (INNER, OUTER, etc) таблицы к себе.

присоединяется на основе операторов

в зависимости от оператора, используемого для JOIN предложение, может быть два типа JOINs. Они

  1. Equi присоединиться
  2. тета присоединиться

1. Equi JOIN:

на все JOIN типа (INNER, OUTER и т. д.), Если мы используем только оператор равенства ( = ), то мы говорим что the JOIN это EQUI JOIN.

2. Тета присоединиться:

это же EQUI JOIN но это позволяет всем другим операторам, таким как >, = etc.

многие считают как EQUI JOIN и тета JOIN аналогично INNER, OUTER и т. д. JOINs. Но я твердо верю, что это ошибка и делает идеи расплывчатые. Потому что INNER JOIN, OUTER JOIN etc все связаны с таблицы и их данные, тогда как EQUI JOIN и THETA JOIN только связано с операторами, которые мы используем в первом.

опять же, есть много тех, кто считает NATURAL JOIN как какой-то "странно"EQUI JOIN. На самом деле, это правда, из-за первого условие, которое я упомянул для NATURAL JOIN. Однако, мы не должны ограничьте это просто NATURAL JOIN s в одиночку. INNER JOINs,OUTER JOINs etc может быть EQUI JOIN тоже.


определение:


соединения-это способ запроса данных, которые объединены вместе из нескольких таблиц одновременно.

типы соединений:


к РСУБД относятся 5-типы соединений:

  • Equi-Join: объединяет общие записи из двух таблиц на основе условия равенства. Технически, объединение производится с помощью оператора равенства ( = ) для сравнения значений PrimaryKey одна таблица и Foriegn Ключевые значения таблицы antoher, следовательно, результирующий набор включает общие (согласованные) записи из обеих таблиц. Для реализации см. INNER-JOIN.

  • Natural-Регистрация: это расширенная версия Equi-Join, в которой выберите операция пропускает повторяющийся столбец. Для реализации см. INNER-JOIN

  • Non-Equi-Join: это обратный Equi-join, где условие соединения используется иначе, чем равно оператор (=) e.Джи !=, =, >,

  • Self-Join:: настраиваемое поведение соединения, где таблица объединена с собой; это обычно необходимо для запроса само-ссылочных таблиц (или унарной сущности отношения). Для реализации см. INNER-JOINs.

  • Декартова Произведения: он кросс объединяет все записи обеих таблиц без каких-либо условий. Технически он возвращает результирующий набор запроса без предложения WHERE -.

в соответствии с проблемой SQL и продвижением, есть 3 типа соединений, и все соединения СУБД могут быть achvied, используя эти типы соединений.

  1. INNER-JOIN: он объединяет (или комбинирует) совпадающие строки из двух таблиц. Сопоставление выполняется на основе общих столбцов таблиц и операции их сравнения. Если equaility условие тогда: эквисоединение выполняется, в противном случае не EQUI-Join.

  2. **OUTER-JOIN: * * он объединяет (или объединяет) совпадающие строки из двух таблиц и непревзойденные строки со значениями NULL. Тем не менее, можно настроить выбор несогласованных строк e.g, выбор непревзойденной строки из первой таблицы или второй таблицы по подтипам: левое внешнее соединение и правое внешнее соединение.

    2.1. левое внешнее соединение (a.к. a, left-JOIN): возвращает сопоставленные строки из двух таблиц и не сопоставляется с левой таблицей (i.e, первая таблица) только.

    2.2. правое внешнее соединение (a.к. a, RIGHT-JOIN): возвращает сопоставленные строки из двух таблиц и не сопоставленные только из правой таблицы.

    2.3. ПОЛНОЕ ВНЕШНЕЕ СОЕДИНЕНИЕ (a.к. A OUTER JOIN): возвращает совпадение и несоответствие из обеих таблиц.

  3. ПЕРЕКРЕСТНОЕ ПРИСОЕДИНЕНИЕ: это соединение не сливается/combiens вместо этого он выполняет cartisian продукт.

enter image description here Примечание: Self-JOIN может быть достигнуто либо внутренним соединением, либо внешним соединением и перекрестным соединением на основе требования, но таблица должна присоединиться к себе.

дополнительные сведения:

примеры:

1.1: INNER-JOIN: Equi-join implemetation

SELECT  *
FROM Table1 A 
 INNER JOIN Table2 B ON A.<PrimaryKey> =B.<ForeignKey>;

1.2: INNER-JOIN: Natural-Регистрация реализации

Select A.*, B.Col1, B.Col2          --But no B.ForiengKyeColumn in Select
 FROM Table1 A
 INNER JOIN Table2 B On A.Pk = B.Fk;

1.3: внутреннее объединение с Eqijoin реализация

Select *
 FROM Table1 A INNER JOIN Table2 B On A.Pk <= B.Fk;

1.4: внутренний-присоединяйтесь к SELF-JOIN

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: CROSS JOIN

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;

интересно, что большинство других ответов страдают от этих двух проблем:

недавно я написал статью на эту тему:вероятно, неполное, Полное руководство по многим различным способам объединения таблиц в SQL, которые я суммировать здесь.

прежде всего: соединения являются декартовыми продуктами

вот почему диаграммы Венна объясняют их так неточно, потому что соединение создает декартова произведения между двумя соединенными столами. Википедия прекрасно иллюстрирует это:

enter image description here

синтаксис 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

который объединяет все строки из одной таблицы со всеми строками с другого стола:--61-->

источник:

+--------+   +------------+
| 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

внутреннее соединение (тета-соединение)

An 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 Доступ.)

(посмотрите на статью для примеров результатов)

EQUI ПРИСОЕДИНИТЬСЯ

особый вид тета-соединения-это equi 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() синтаксис позволяет указать столбец, который должен присутствовать по обе стороны таблиц операции соединения, и создает предикат равенства для этих двух столбцов.

ЕСТЕСТВЕННОЕ СОЕДИНЕНИЕ

другие ответы перечислили этот" тип соединения " отдельно, но это не имеет смысла. Это просто синтаксическая форма сахара для equi 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 соответствует чему-либо на "другой стороне соединения". Короче говоря, это заполнение разреженных данных в отчетах. Очень полезно!

SEMI JOIN

серьезно? Другого ответа не было? Конечно, нет!--168-->потому что у него нет собственного синтаксиса в 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 сказуемое выступает в качестве предикат semi join. Если вы не верите в это, ознакомьтесь с планами выполнения, например, в Oracle. Вы увидите, что база данных выполняет операцию ПОЛУСОЕДИНЕНИЯ, а не EXISTS() сказуемое.

enter image description here

АНТИ ПРИСОЕДИНИТЬСЯ

это как раз противоположность SEMI JOIN (будьте осторожны, чтобы не использовать NOT IN хотя, так как имеет важное предостережение)

вот все актеры без фильмы:

SELECT *
FROM actor a
WHERE NOT EXISTS (
  SELECT * FROM film_actor fa
  WHERE a.actor_id = fa.actor_id
)

некоторые люди (особенно люди MySQL) также пишут ANTI JOIN следующим образом:

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-per-something,LATERAL JOIN будет вашим другом. Если вы SQL Серверный человек, тогда вы знаете это 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

хорошо, возможно, это обман, потому что LATERAL JOIN или APPLY выражение действительно является "коррелированным подзапросом", который создает несколько строк. Но если мы допустим "коррелированные подзапросы", мы также можем говорить об этом...

MULTISET

это действительно реализовано только 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

как вы видели, есть больше типов соединения, чем просто "скучно"INNER, OUTER и CROSS JOIN это обычно упоминается. подробнее в моей статье. И, пожалуйста, прекратите использовать диаграммы Venn для иллюстрации их.


в SQL server существуют различные типы соединений.

  1. КРЕСТ ПРИСОЕДИНИТЬСЯ
  2. ВНУТРЕННЕЕ СОЕДИНЕНИЕ
  3. ВНЕШНЕЕ СОЕДИНЕНИЕ

внешние соединения снова делятся на 3 типа

  1. левое соединение или левое внешнее соединение
  2. правое соединение или правое внешнее соединение
  3. полное соединение или полный внешний Присоединяйтесь

enter image description here

enter image description here

JOIN или внутреннее соединение

SELECT Name, Gender, Salary, DepartmentName
FROM tblEmployee
INNER JOIN tblDepartment
ON tblEmployee.DepartmentId = tblDepartment.Id

OR

SELECT Name, Gender, Salary, DepartmentName
FROM tblEmployee
JOIN tblDepartment
ON tblEmployee.DepartmentId = tblDepartment.Id

enter image description here

левое соединение или левое внешнее соединение

SELECT Name, Gender, Salary, DepartmentName
FROM tblEmployee
LEFT OUTER JOIN tblDepartment
ON tblEmployee.DepartmentId = tblDepartment.Id

OR

SELECT Name, Gender, Salary, DepartmentName
FROM tblEmployee
LEFT JOIN tblDepartment
ON tblEmployee.DepartmentId = tblDepartment.Id

enter image description here

правое соединение или правое внешнее соединение

SELECT Name, Gender, Salary, DepartmentName
FROM tblEmployee
RIGHT OUTER JOIN tblDepartment
ON tblEmployee.DepartmentId = tblDepartment.Id

OR

SELECT Name, Gender, Salary, DepartmentName
FROM tblEmployee
RIGHT JOIN tblDepartment
ON tblEmployee.DepartmentId = tblDepartment.Id

enter image description here

полное соединение или полный внешний Присоединяйтесь

SELECT Name, Gender, Salary, DepartmentName
FROM tblEmployee
FULL OUTER JOIN tblDepartment
ON tblEmployee.DepartmentId = tblDepartment.Id

OR

SELECT Name, Gender, Salary, DepartmentName
FROM tblEmployee
FULL JOIN tblDepartment
ON tblEmployee.DepartmentId = tblDepartment.Id

enter image description here

enter image description here

enter image description here


Я создал иллюстрацию, которая объясняет лучше, чем слова, на мой взгляд: SQL Join table of explanation


Я собираюсь нажать мой питомец peeve: ключевое слово USING.

если обе таблицы с обеих сторон соединения имеют свои внешние ключи с правильным именем (т. е. с тем же именем, а не только " id), то это можно использовать:

SELECT ...
FROM customers JOIN orders USING (customer_id)

Я нахожу это очень практичным, читаемым и недостаточно часто используемым.