Ответы:
SQL JOIN
?SQL JOIN
это метод для извлечения данных из двух или более таблиц базы данных.
SQL JOIN
с?Всего пять 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
(см .: Различие между 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
является 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
s.INNER JOIN
s,OUTER JOIN
s и тEQUI JOIN
. д. тоже могут быть .
Определение:
СОЕДИНЕНИЯ - это способ запроса данных, которые объединяются из нескольких таблиц одновременно.
К СУБД относятся 5 типов соединений:
Equi-Join: объединяет общие записи из двух таблиц на основе условия равенства. Технически, соединение выполняется с использованием оператора равенства (=) для сравнения значений первичного ключа одной таблицы и значений внешнего ключа другой таблицы, поэтому набор результатов включает в себя общие (совпавшие) записи из обеих таблиц. Для реализации см. INNER-JOIN.
Natural-Join: это расширенная версия Equi-Join, в которой операция SELECT пропускает повторяющийся столбец. Для реализации см. INNER-JOIN
Non-Equi-Join: это обратное Equi-Join, где используется условие соединения, отличное от оператора равенства (=), например,! =, <=,> =,>, <Или BETWEEN и т. Д. Для реализации см. INNER-JOIN.
Self-Join: настраиваемое поведение объединения, когда таблица объединяется с самим собой; Как правило, это необходимо для запроса к самоссылающимся таблицам (или сущности унарных отношений). Для реализации см. ВНУТРЕННИЕ СОЕДИНЕНИЯ.
Декартово произведение: оно объединяет все записи обеих таблиц без каких-либо условий. Технически он возвращает набор результатов запроса без WHERE-предложения.
В соответствии с требованиями и усовершенствованиями SQL существует 3 типа объединений, и все объединения RDBMS могут быть достигнуты с помощью этих типов объединений.
INNER-JOIN: объединяет (или объединяет) совпадающие строки из двух таблиц. Сопоставление выполняется на основе общих столбцов таблиц и их операции сравнения. Если условие на основе равенства, то: EQUI-JOIN выполняется, в противном случае не-EQUI-Join.
OUTER-JOIN: объединяет (или объединяет) совпавшие строки из двух таблиц и несопоставленные строки со значениями NULL. Однако можно настроить выбор несопоставленных строк, например, выбрать несопоставленную строку из первой или второй таблицы по подтипам: LEFT OUTER JOIN и RIGHT OUTER JOIN.
2.1. LEFT Outer JOIN (он же LEFT-JOIN): возвращает совпадающие строки из двух таблиц и не совпадает только с таблицей LEFT (т. Е. С первой таблицей).
2.2. RIGHT Outer JOIN (он же RIGHT-JOIN): возвращает совпадающие строки из двух таблиц и не совпадает только с правой таблицей.
2,3. FULL OUTER JOIN (он же OUTER JOIN): возвращает совпадения и несоответствия из обеих таблиц.
CROSS-JOIN: это объединение не объединяет / объединяет, а выполняет декартово произведение.
Примечание: Self-JOIN может быть достигнуто с помощью INNER-JOIN, OUTER-JOIN и CROSS-JOIN в зависимости от требований, но таблица должна соединяться сама с собой.
Чтобы получить больше информации:
1.1: INNER-JOIN: реализация Equi-Join
SELECT *
FROM Table1 A
INNER JOIN Table2 B ON A.<Primary-Key> =B.<Foreign-Key>;
1.2: INNER-JOIN: реализация 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-join
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: 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;
intersect
/ except
/ union
; здесь кружки - это строки, возвращаемые символом left
& right
join
, как говорят пронумерованные метки. Картинка AXB - это чепуха. cross join
= inner join on 1=1
& является частным случаем первой диаграммы.
UNION JOIN
. Теперь устарел в SQL: 2003.
Интересно, что большинство других ответов страдают от этих двух проблем:
Недавно я написал статью на эту тему: «Вероятно, неполное, всеобъемлющее руководство по множеству различных способов соединения таблиц в SQL» , которую я здесь кратко изложу.
Вот почему диаграммы Венна объясняют их так неточно, потому что JOIN создает декартово произведение между двумя объединенными таблицами. Википедия это прекрасно иллюстрирует:
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
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 Access).
( посмотрите на статью для примеров результатов )
Специальный вид Theta-JOIN - это 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()
Синтаксис позволяет указать столбец , который должен присутствовать на каждой стороне JOIN таблицы операции и создает предикат равенства этих двух столбцов.
Другие ответы перечислили этот «тип JOIN» отдельно, но это не имеет смысла. Это просто синтаксическая форма сахара для equi JOIN, которая является частным случаем Theta-JOIN или INNER 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) также пишут ANTI JOIN так:
SELECT *
FROM actor a
LEFT JOIN film_actor fa
USING (actor_id)
WHERE film_id IS NULL
Я думаю, что историческая причина - производительность.
О, Боже, это слишком круто. Я единственный, кто упомянул это? Вот крутой запрос:
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
Хорошо, возможно, это обман, потому что выражение 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)
Я нахожу это очень практичным, читабельным и недостаточно часто используемым.