Как выполнить ПОЛНОЕ ВНЕШНЕЕ СОЕДИНЕНИЕ в MySQL?

Я хочу выполнить полное внешнее соединение в MySQL. Это возможно? Поддерживается ли полное внешнее соединение MySQL?


person Spencer    schedule 25.01.2011    source источник
comment
возможный дубликат ошибки синтаксиса полного внешнего соединения MySQL   -  person Joe Stefanelli    schedule 25.01.2011
comment
На этот вопрос есть лучшие ответы   -  person Julio Marins    schedule 03.11.2014
comment
Остерегайтесь ответов здесь. Стандарт SQL говорит, что полное соединение - это внутреннее соединение по объединению строк, все несогласованные строки левой таблицы, расширенные нулевыми значениями, объединяют все правые строки таблицы, расширенные нулями. Большинство ответов здесь неверны (см. Комментарии), а те, которые не ошибаются, не относятся к общему случаю. Хотя есть много (необоснованных) голосов. (См. Мой ответ.)   -  person philipxy    schedule 12.08.2018
comment
Что насчет того, когда вы пытаетесь присоединиться по непервичным ключам / сгруппированным столбцам? например, у меня есть запрос о продажах по штатам, продаж и другой о расходах по штатам, расходах, оба запроса используют группу по (состояние). Когда я объединяю левое и правое соединения между двумя запросами, я получаю несколько строк с продажами, но без расходов, еще несколько с расходами, но без продаж, все вплоть до этого момента, но я также получаю несколько строк с обоими продажи и расходы и повторяющаяся колонка состояния ... не большая проблема, но кажется неправильным ...   -  person Jairo Lozano    schedule 12.04.2019
comment
@JairoLozano Ограничения не нужны для запроса. Хотя, когда ограничения содержат дополнительные запросы, они возвращают желаемый ответ, которого в противном случае не было бы. Ограничения не влияют на полное соединение при возврате данных аргументов. Проблема, которую вы описываете, заключается в том, что вы написали неправильный запрос. (Предположительно распространенная ошибка, когда люди хотят некоторых объединений, каждое из которых может включать другой ключ, некоторых подзапросов, каждый из которых, возможно, связан с объединением и / или агрегацией, но они ошибочно пытаются выполнить все объединение, а затем все агрегации или агрегацию по предыдущим агрегациям .)   -  person philipxy    schedule 07.02.2020
comment
все ответы на UNION вместо UNION ALL неверны. все ответы с подзапросами или 3 объединенными выборками неэффективны. правильные ответы будут объединять все левое соединение с выбором из второй таблицы с где не существует в первой таблице (или эквивалентное внешнее соединение + где = условие NULL)   -  person ysth    schedule 16.08.2020


Ответы (14)


У вас нет ПОЛНЫХ СОЕДИНЕНИЙ в MySQL, но вы можете быть уверены: подражайте им.

Для ОБРАЗЦА кода, записанного с этого вопроса SO, у вас есть:

с двумя таблицами t1, t2:

SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id

Вышеупомянутый запрос работает для особых случаев, когда операция FULL OUTER JOIN не приведет к созданию повторяющихся строк. Вышеупомянутый запрос зависит от оператора UNION set для удаления повторяющихся строк, представленных шаблоном запроса. Мы можем избежать появления повторяющихся строк, используя шаблон anti-join для второго запроса, а затем используя оператор множества UNION ALL для объединения двух наборов. В более общем случае, когда ПОЛНОЕ ВНЕШНЕЕ СОЕДИНЕНИЕ будет возвращать повторяющиеся строки, мы можем сделать это:

SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION ALL
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id
WHERE t1.id IS NULL
person Pablo Santa Cruz    schedule 25.01.2011
comment
На самом деле то, что вы написали, неверно. Потому что, когда вы выполняете UNION, вы удаляете дубликаты, а иногда, когда вы объединяете две разные таблицы, должны быть дубликаты. - person Pavle Lekic; 19.03.2013
comment
Это правильный пример: (SELECT ... FROM tbl1 LEFT JOIN tbl2 ...) UNION ALL (SELECT ... FROM tbl1 RIGHT JOIN tbl2 ... WHERE tbl1.col IS NULL) - person Pavle Lekic; 19.03.2013
comment
Итак, разница в том, что я выполняю левое включающее соединение, а затем правое исключительное, используя UNION ALL - person Pavle Lekic; 19.03.2013
comment
@PavleLekic: вы просто ошибаетесь во всех учетных записях, поскольку вы никогда не хотите никаких дубликатов при ПОЛНОМ ВНЕШНЕМ СОЕДИНЕНИИ (в противном случае это совершенно другой тип соединения) - person Nikola Bogdanović; 15.11.2013
comment
@PavleLekic: его пример вполне верен, а ваш - то же самое (хотя и более эффективно, поскольку UNION ALL намного быстрее, чем отдельный UNION - tbl1.col должен быть столбцом соединения tbl1.id) - нет никакой разницы в результатах - person Nikola Bogdanović; 15.11.2013
comment
@PavleLekic: единственный способ, которым его ответ может быть неправильным, - это если столбец соединения не является уникальным (будет меньше результатов, чем в вашем примере), но вы никогда не должны делать это в ПОЛНОМ ВНЕШНЕМ СОЕДИНЕНИИ - person Nikola Bogdanović; 15.11.2013
comment
@ NikolaBogdanović: безусловно, есть разница, если то, что вы используете, не является уникальным ключом. скажем, у t2 было две строки с одинаковым идентификатором, а у t1 было ноль или одна строка с этим идентификатором; ваш запрос UNION дает только одну строку результатов; правильный запрос SELECT * FROM t1 LEFT JOIN t2 ON t1.id = t2.id UNION ALL SELECT * FROM t1 RIGHT JOIN t2 ON t1.id = t2.id WHERE t1.id IS NULL дает два. - person ysth; 01.04.2014
comment
и теперь я вижу, что вы сами это говорите, извините. Возможно, вы могли бы обновить свой ответ, учитывая, что в этом случае он ошибается и UNION ALL всегда будет более эффективным? - person ysth; 01.04.2014
comment
@PavleLekic Ваша критика обоснована, но ваш «правильный» пример неясен (как нам выбрать, какой столбец проверять на NULL в предложении WHERE?) и по-прежнему не могут идеально имитировать внешнее соединение. Например, он может завершиться неудачно, если у вас есть условие эксцентрического соединения, например, если все столбцы обеих таблиц имеют значение NULL. - person Mark Amery; 31.08.2014
comment
создать таблицу t1 (id int); создать таблицу t2 (id int); вставить в значения t1 (1); вставить в значения t1 (1); Тогда правильный результат состоит из двух строк. Это решение с UNION удаляет дубликаты, т.е. работает не во всех случаях. - person jarlh; 30.12.2014
comment
@jarth. Вы правы, версия UNION может привести к неверному результату, но только если одна из двух таблиц не имеет первичного ключа или ограничений уникальности. (в реляционном смысле можно утверждать, что в этом случае они не являются правильными таблицами;) - person ypercubeᵀᴹ; 11.04.2015
comment
@ypercube: если в t1 и t2 нет повторяющихся строк, запрос в этом ответе действительно возвращает набор результатов, который имитирует ПОЛНОЕ ВНЕШНЕЕ СОЕДИНЕНИЕ. Но в более общем случае, например, список SELECT не содержит достаточного количества столбцов / выражений, чтобы сделать возвращаемые строки уникальными, тогда этот шаблон запроса недостаточен для воспроизведения набора, который был бы создан а FULL OUTER JOIN. Чтобы добиться более точной эмуляции, нам понадобится оператор set UNION ALL, а для одного из запросов потребуется шаблон anti-join. Комментарий от Павла Лекича (выше) дает правильный шаблон запроса. - person spencer7593; 07.05.2015
comment
@MarkAmery спросил, как мы выбираем какой столбец (или выражение) для проверки наличия NULL в антисоединении? Мы просто выбираем столбец (или выражение) в возвращаемом наборе, который будет гарантированно отличаться от NULL, если будет найдена соответствующая строка. И это довольно легко сделать, если предикат соединения представляет собой сравнение равенства в столбце, тогда мы гарантированно, что любая возвращенная соответствующая строка будет иметь значение, отличное от NULL в этот столбец. Единственными строками, которые будут иметь значение NULL, будут строки, у которых нет совпадений. (Если предикат соединения соответствует значениям NULL, мы должны найти выражение, которое не является NULL) - person spencer7593; 07.05.2015
comment
@ spencer7593 не знаю, зачем ты меня пингуешь;) - person ypercubeᵀᴹ; 07.05.2015
comment
Как это совместить с ORDER BY? Он жалуется, что я не могу использовать эти таблицы в своем глобальном ЗАКАЗЕ. - person still_dreaming_1; 12.11.2015
comment
Я понял. Мне просто нужно было использовать только имена столбцов вместо имени таблицы и имени столбца в ORDER BY. - person still_dreaming_1; 12.11.2015
comment
@PavleLekic По крайней мере, в моей системе UNION ALL всегда создает дополнительные повторяющиеся строки, даже если в двух таблицах нет дубликатов, и обеспечивает это с помощью первичных ключей. Вам всегда нужно использовать UNION без ALL, если вы хотите получить уникальные строки. Это имеет смысл, если вы просто думаете о том, что делают UNION и UNION ALL, не думая о попытках имитировать полное внешнее соединение. Чтобы проиллюстрировать это, в случае, когда каждая строка случайно имеет совпадающую строку в другой таблице (совпадение, как в поле id, используемом для предложения on, такое же), UNION ALL выберет вдвое больше строк, чем UNION. - person still_dreaming_1; 12.11.2015
comment
если t1 и t2 являются анонимными запросами, есть ли способ отформатировать этот запрос, не повторяя выбор для t1 и t2? - person Cruncher; 19.01.2017
comment
запрос UNION завершится ошибкой, если одна из двух таблиц не имеет первичного ключа или ограничений уникальности, но он также завершится ошибкой, если SELECT возвращает только подмножество столбцов вместо *. Я понимаю, что запрос UNION должен работать, и если он не работает, возникает проблема в реляционных терминах в другом месте, но я думаю, что общий способ воспроизведения FULL OUTER JOIN - это UNION ALL запрос с шаблоном сглаживания - person fthiella; 31.03.2017
comment
Я откатился к исходному ответу (union all без шаблона антисоединения - это просто неправильно, а union запрос каким-то образом, но не всегда, правильный ... Я все еще считаю правильным ответ union all с шаблоном антисоединения на любом из первых или второй запрос ...) - person fthiella; 03.04.2017
comment
Я получил Can't reopen table - person tom10271; 16.04.2018
comment
@GordonLinoff, постоянный гуру SQL в SO, говорит, что это не лучший ответ, потому что он не обрабатывает дубликаты правильно. Можете ли вы вместо этого принять его ответ? Я уже много лет использую этот вопрос как канонический обман. - person Barmar; 24.01.2020
comment
Я бы с удовольствием удалил свой ответ, если это поможет. Но богатый обмен мнениями, произошедший по поводу комментариев, заставляет меня думать, что не стоит. @Barmar - person Pablo Santa Cruz; 26.01.2020

Ответ, который дал Пабло Санта-Крус, правильный; однако, если кто-то наткнулся на эту страницу и хочет получить больше разъяснений, вот подробная разбивка.

Примеры таблиц

Предположим, у нас есть следующие таблицы:

-- t1
id  name
1   Tim
2   Marta

-- t2
id  name
1   Tim
3   Katarina

Внутренние соединения

Внутреннее соединение, например:

SELECT *
FROM `t1`
INNER JOIN `t2` ON `t1`.`id` = `t2`.`id`;

Мы получим только записи, которые появляются в обеих таблицах, например:

1 Tim  1 Tim

Внутренние соединения не имеют направления (например, влево или вправо), потому что они явно двунаправленные - нам требуется совпадение с обеих сторон.

Внешние соединения

С другой стороны, внешние соединения предназначены для поиска записей, которым может не соответствовать другая таблица. Таким образом, вы должны указать, на какой стороне соединения может быть пропущенная запись.

LEFT JOIN и RIGHT JOIN являются сокращениями для LEFT OUTER JOIN и RIGHT OUTER JOIN; Я буду использовать их полные имена ниже, чтобы усилить концепцию внешних соединений и внутренних соединений.

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

Левое внешнее соединение, например:

SELECT *
FROM `t1`
LEFT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`;

... даст нам все записи из левой таблицы независимо от того, совпадают ли они в правой таблице, например:

1 Tim   1    Tim
2 Marta NULL NULL

Правое внешнее соединение

Правое внешнее соединение, например:

SELECT *
FROM `t1`
RIGHT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`;

... даст нам все записи из правой таблицы независимо от того, совпадают ли они в левой таблице, например:

1    Tim   1  Tim
NULL NULL  3  Katarina

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

Полное внешнее соединение даст нам все записи из обеих таблиц, независимо от того, совпадают они или нет в другой таблице, с NULL на обеих сторонах, где нет совпадений. Результат будет выглядеть так:

1    Tim   1    Tim
2    Marta NULL NULL
NULL NULL  3    Katarina

Однако, как указал Пабло Санта-Крус, MySQL не поддерживает это. Мы можем сымитировать это, выполнив UNION левого и правого соединения, например:

SELECT *
FROM `t1`
LEFT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`

UNION

SELECT *
FROM `t1`
RIGHT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`;

Вы можете думать о UNION как о значении «выполнить оба этих запроса, а затем сложить результаты друг над другом»; некоторые строки будут получены из первого запроса, а некоторые - из второго.

Следует отметить, что UNION в MySQL устранит точные дубликаты: Тим будет присутствовать здесь в обоих запросах, но результат UNION перечисляет его только один раз. Мой коллега-гуру баз данных считает, что на такое поведение нельзя полагаться. Чтобы быть более точным, мы могли бы добавить предложение WHERE во второй запрос:

SELECT *
FROM `t1`
LEFT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`

UNION

SELECT *
FROM `t1`
RIGHT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`
WHERE `t1`.`id` IS NULL;

С другой стороны, если вы по какой-то причине хотите увидеть дубликаты, вы можете использовать UNION ALL.

person Nathan Long    schedule 09.02.2012
comment
Для MySQL вы действительно хотите избежать использования UNION вместо UNION ALL, если нет перекрытия (см. Комментарий Павла выше). Если бы вы могли добавить дополнительную информацию по этому поводу в свой ответ здесь, я думаю, что это был бы предпочтительный ответ на этот вопрос, поскольку он более подробный. - person Garen; 12.02.2014
comment
Рекомендация коллеги-гуру баз данных верна. Что касается реляционной модели (вся теоретическая работа, проделанная Тедом Коддом и Крисом Дейтом), запрос последней формы имитирует ПОЛНОЕ ВНЕШНЕЕ СОЕДИНЕНИЕ, поскольку он объединяет два различных набора. Второй запрос не вводит дубликатов (строки уже возвращенный первым запросом), который не будет создан FULL OUTER JOIN. Нет ничего плохого в том, чтобы делать запросы таким образом и использовать UNION для удаления этих дубликатов. Но чтобы действительно воспроизвести FULL OUTER JOIN, нам нужно, чтобы один из запросов был антисоединением. - person spencer7593; 07.05.2015
comment
Сообщение Джеффа Этвуда действительно хорошо объясняет типы соединений с помощью диаграмм Венна. И он приводит пример анти-соединения ... возврата строк из A, которых нет в B. И мы можем визуально увидеть, как этот набор отличается от набора всех строк в B вместе с совпадающими строками в A. - person spencer7593; 07.05.2015
comment
@NathanLong, ваш пост здесь старый, но, черт возьми, это отличное небольшое разъяснение и инструкция. хорошо написано и подтверждено на примерах! Потрясающие. - person Ken; 23.10.2016
comment
@ spencer7593, не могли бы вы объяснить, почему нам нужно то, что рекомендуют коллеги-гуру, то есть предложение where во втором select запросе WHERE `t1`.`id` IS NULL;? - person Istiaque Ahmed; 06.11.2017
comment
@IstiaqueAhmed: цель - имитировать операцию ПОЛНОГО ВНЕШНЕГО СОЕДИНЕНИЯ. Нам нужно это условие во втором запросе, чтобы он возвращал только строки, для которых нет совпадений (шаблон антисоединения). Без этого условия запрос является внешним соединением ... он возвращает как совпадающие, так и не совпадающие строки. И соответствующие строки были уже возвращены первым запросом. Если второй запрос возвращает те же самые строки (снова), мы продублировали строки, и наш результат не будет эквивалентен ПОЛНОМУ ВНЕШНЕМ СОЕДИНЕНИЮ. - person spencer7593; 06.11.2017
comment
@IstiaqueAhmed: Верно, что операция UNION удалит эти дубликаты; но он также удаляет ВСЕ повторяющиеся строки, включая повторяющиеся строки, которые были бы возвращены FULL OUTER JOIN. Чтобы подражать a FULL JOIN b, правильный шаблон - (a LEFT JOIN b) UNION ALL (b ANTI JOIN a). - person spencer7593; 06.11.2017
comment
@ spencer7593, including duplicate rows that would be in the returned by a FULL OUTER JOIN - не могли бы вы объяснить это поподробнее? - person Istiaque Ahmed; 06.11.2017
comment
Очень краткий ответ с отличным объяснением. Спасибо за это. - person Najeeb; 22.02.2019
comment
отличное объяснение, но UNION вместо UNION ALL удаляет повторяющиеся строки, когда FULL OUTER JOIN не имеет - person ysth; 16.08.2020

Использование запроса union удалит дубликаты, и это отличается от поведения full outer join, который никогда не удаляет дубликаты:

[Table: t1]                            [Table: t2]
value                                  value
-------                                -------
1                                      1
2                                      2
4                                      2
4                                      5

Это ожидаемый результат full outer join:

value | value
------+-------
1     | 1
2     | 2
2     | 2
Null  | 5
4     | Null
4     | Null

Это результат использования left и right Join с union:

value | value
------+-------
Null  | 5 
1     | 1
2     | 2
4     | Null

[SQL Fiddle]

Мой предлагаемый запрос:

select 
    t1.value, t2.value
from t1 
left outer join t2  
  on t1.value = t2.value
union all      -- Using `union all` instead of `union`
select 
    t1.value, t2.value
from t2 
left outer join t1 
  on t1.value = t2.value
where 
    t1.value IS NULL 

Результат вышеуказанного запроса такой же, как и ожидаемый результат:

value | value
------+-------
1     | 1
2     | 2
2     | 2
4     | NULL
4     | NULL
NULL  | 5

[SQL Fiddle]


@Steve Chambers: [Из комментариев, большое спасибо!]
< strong> Примечание. Это может быть лучшим решением как с точки зрения эффективности, так и с точки зрения получения тех же результатов, что и FULL OUTER JOIN. Это сообщение в блоге также хорошо это объясняет - процитирую метод 2: "Это обрабатывает повторяющиеся строки правильно и не включает ничего, чего не должно быть. Необходимо использовать UNION ALL вместо простого UNION, что устранит нужные мне дубликаты. сохранить. Это может быть значительно более эффективным для больших наборов результатов, поскольку нет необходимости сортировать и удалять дубликаты ".


Я решил добавить еще одно решение, основанное на full outer join визуализации и математике, оно не лучше, чем указано выше, но более читабельно:

Полное внешнее соединение означает (t1 ∪ t2): все в t1 или в t2
(t1 ∪ t2) = (t1 ∩ t2) + t1_only + t2_only: все в t1 и t2 плюс все в t1, которых нет в t2, и плюс все в t2, которых нет в t1:

-- (t1 ∩ t2): all in both t1 and t2
select t1.value, t2.value
from t1 join t2 on t1.value = t2.value    
union all  -- And plus 
-- all in t1 that not exists in t2
select t1.value, null
from t1
where not exists( select 1 from t2 where t2.value = t1.value)    
union all  -- and plus
-- all in t2 that not exists in t1
select null, t2.value
from t2
where not exists( select 1 from t1 where t2.value = t1.value)

[SQL Fiddle]

person shA.t    schedule 28.05.2015
comment
Мы выполняем одну и ту же задачу два раза. Если есть подзапрос для t1 и t2, тогда mysql должен выполнять одну и ту же задачу несколько раз, не так ли? Можем ли мы удалить это с помощью псевдонима в этой ситуации ?: - person Kabir Hossain; 13.10.2015
comment
Предлагаю вам использовать временные таблицы;). - person shA.t; 13.10.2015
comment
Этот метод кажется лучшим решением как с точки зрения эффективности, так и с точки зрения получения тех же результатов, что и FULL OUTER JOIN. Это сообщение в блоге также хорошо это объясняет - цитируя метод 2: Он правильно обрабатывает повторяющиеся строки и не включает ничего, чего не должно быть. Необходимо использовать UNION ALL вместо обычного UNION, чтобы удалить дубликаты, которые я хочу сохранить. Это может быть значительно более эффективным для больших наборов результатов, поскольку нет необходимости сортировать и удалять дубликаты. - person Steve Chambers; 22.07.2016
comment
@SteveChambers уже слишком поздно, но спасибо за ваш комментарий. Я добавил ваш комментарий, а затем ответил, чтобы выделить больше, Если вы не согласны, пожалуйста, откатите его назад;). - person shA.t; 07.09.2017
comment
Нет проблем @ shA.t - ИМО, это действительно должно иметь больше голосов и / или быть принятым ответом. - person Steve Chambers; 07.09.2017
comment
первые два выбора могут быть более эффективно выполнены с помощью одного выбора, выполняющего левое соединение - person ysth; 16.08.2020

MySql не имеет синтаксиса FULL-OUTER-JOIN. Вы должны подражать, выполняя как LEFT JOIN, так и RIGHT JOIN следующим образом:

SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id  
UNION
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id

Но MySql также не имеет синтаксиса RIGHT JOIN. Согласно упрощению внешнего соединения MySql, право join преобразуется в эквивалентное левое соединение путем переключения t1 и t2 в предложениях FROM и ON в запросе. Таким образом, MySql Query Optimizer переводит исходный запрос в следующее:

SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id  
UNION
SELECT * FROM t2
LEFT JOIN t1 ON t2.id = t1.id

Теперь нет ничего плохого в том, чтобы написать исходный запрос как есть, но скажите, если у вас есть предикаты, такие как предложение WHERE, которое является before-join или предикат AND в предложении ON, который является во время присоединения, тогда вы можете захотеть взгляни на дьявола; что в деталях.

Оптимизатор запросов MySql регулярно проверяет предикаты, если они отклонены с нулевым значением. «Определение Теперь, если вы выполнили ПРАВИЛЬНОЕ СОЕДИНЕНИЕ, но с предикатом WHERE для столбца из t1, вы можете столкнуться с риском столкновения с отклоненным нулевым значением сценарий.

Например, следующий запрос -

SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
WHERE t1.col1 = 'someValue'
UNION
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id
WHERE t1.col1 = 'someValue'

Оптимизатор запросов переводит на следующее:

SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
WHERE t1.col1 = 'someValue'
UNION
SELECT * FROM t2
LEFT JOIN t1 ON t2.id = t1.id
WHERE t1.col1 = 'someValue'

Таким образом, порядок таблиц изменился, но предикат по-прежнему применяется к t1, но t1 теперь находится в предложении 'ON'. Если t1.col1 определен как столбец NOT NULL, то этот запрос будет отклонен с нулевым значением.

Любое внешнее соединение (левое, правое, полное), которое отклонено нулевым значением, преобразуется MySql во внутреннее соединение.

Таким образом, ожидаемые результаты могут полностью отличаться от того, что возвращает MySql. Вы можете подумать, что это ошибка MySql RIGHT JOIN, но это не так. Именно так работает оптимизатор запросов MySql. Поэтому ответственный разработчик должен обращать внимание на эти нюансы при построении запроса.

person ARK    schedule 12.10.2017

В SQLite вы должны сделать это:

SELECT * 
FROM leftTable lt 
LEFT JOIN rightTable rt ON lt.id = rt.lrid 
UNION
SELECT lt.*, rl.*  -- To match column set
FROM rightTable rt 
LEFT JOIN  leftTable lt ON lt.id = rt.lrid
person Rami Jamleh    schedule 14.01.2013
comment
Можем ли мы его использовать? как: SELECT * FROM leftTable lt LEFT JOIN rightTable rt ON lt.id = rt.lrid UNION SELECT lt. *, rl. * - Для соответствия набору столбцов FROM leftTable lt RIGHT JOIN rightTable rt ON lt.id = rt.lrid ; - person Kabir Hossain; 14.10.2015
comment
да, но SQLite не поддерживает правильные соединения, но да в MYSQL да - person Rami Jamleh; 20.04.2016

Ни один из приведенных выше ответов на самом деле не является правильным, потому что они не соответствуют семантике при наличии повторяющихся значений.

Для такого запроса, как (из этого дубликата):

SELECT * FROM t1 FULL OUTER JOIN t2 ON t1.Name = t2.Name;

Правильный эквивалент:

SELECT t1.*, t2.*
FROM (SELECT name FROM t1 UNION  -- This is intentionally UNION to remove duplicates
      SELECT name FROM t2
     ) n LEFT JOIN
     t1
     ON t1.name = n.name LEFT JOIN
     t2
     ON t2.name = n.name;

Если вам нужно, чтобы это работало со значениями NULL (что также может быть необходимо), используйте NULL-безопасный оператор сравнения, <=>, а не =.

person Gordon Linoff    schedule 14.01.2017
comment
часто это хорошее решение, но оно может давать другие результаты, чем FULL OUTER JOIN, если столбец name имеет значение NULL. Запрос union all с шаблоном анти-соединения должен правильно воспроизводить поведение внешнего соединения, но какое решение является более подходящим, зависит от контекста и от ограничений, которые активны для таблиц. - person fthiella; 31.03.2017
comment
@fthiella. . . Это хороший момент. Я скорректировал ответ. - person Gordon Linoff; 31.03.2017
comment
хорошо, но безопасный с нулевым значением оператор сравнения сделает соединение успешным, что отличается от поведения полного внешнего соединения в случае, если у вас есть нулевые имена как в t1, так и в t2 - person fthiella; 01.04.2017
comment
@fthiella. . . Придется подумать, как это сделать лучше всего. Но учитывая, насколько неверен принятый ответ, почти все может быть ближе к правильному. (Этот ответ неверен, если с обеих сторон есть несколько ключей.) - person Gordon Linoff; 02.04.2017
comment
да, принятый ответ неверен, в качестве общего решения я считаю правильным использовать union all, но в этом ответе отсутствует шаблон антисоединения в первом или втором запросе, который сохранит существующие дубликаты, но не позволит добавлять новые. В зависимости от контекста другие решения (например, это) могут быть более подходящими. - person fthiella; 03.04.2017
comment
похоже, это будет намного менее эффективно, чем просто select t1.name,t2.name from t1 left join t2 using (name) union all select null, name from t2 where not exists (select 1 from t1 where t1.name<=>t2.name); - person ysth; 16.08.2020
comment
@ysth. . . Это не похоже на причину для отрицания этого ответа, особенно если вы посмотрите на историю принятого ответа и поймете, что он получил свои положительные голоса и принятие, когда он был полностью неправильным. - person Gordon Linoff; 16.08.2020

Вы можете сделать следующее:

(SELECT 
    *
FROM
    table1 t1
        LEFT JOIN
    table2 t2 ON t1.id = t2.id
WHERE
    t2.id IS NULL)
UNION ALL
 (SELECT 
    *
FROM
    table1 t1
        RIGHT JOIN
    table2 t2 ON t1.id = t2.id
WHERE
    t1.id IS NULL);
person lolo    schedule 05.06.2018

Изменен запрос shA.t для большей ясности:

-- t1 left join t2
SELECT t1.value, t2.value
FROM t1 LEFT JOIN t2 ON t1.value = t2.value   

    UNION ALL -- include duplicates

-- t1 right exclude join t2 (records found only in t2)
SELECT t1.value, t2.value
FROM t1 RIGHT JOIN t2 ON t1.value = t2.value
WHERE t1.value IS NULL 
person a20    schedule 11.03.2016

Вы можете просто преобразовать полное внешнее соединение, например

SELECT fields
FROM firsttable
FULL OUTER JOIN secondtable ON joincondition

в:

SELECT fields
FROM firsttable
LEFT JOIN secondtable ON joincondition
UNION ALL
SELECT fields (replacing any fields from firsttable with NULL)
FROM secondtable
WHERE NOT EXISTS (SELECT 1 FROM firsttable WHERE joincondition)

Или, если у вас есть хотя бы один столбец, скажем foo, в firsttable, который НЕ равен NULL, вы можете сделать:

SELECT fields
FROM firsttable
LEFT JOIN secondtable ON joincondition
UNION ALL
SELECT fields
FROM firsttable
RIGHT JOIN secondtable ON joincondition
WHERE firsttable.foo IS NULL
person ysth    schedule 17.08.2020

что вы сказали о решении Cross join?

SELECT t1.*, t2.*
FROM table1 t1
INNER JOIN table2 t2 
ON 1=1;
person Super Mario    schedule 13.06.2017
comment
Нет, это перекрестное соединение. Он сопоставит каждую строку в t1 с каждой строкой в ​​t2, давая набор всех возможных комбинаций с select (select count(*) from t1) * (select count(*) from t2)) строками в наборе результатов. - person Marc L.; 24.07.2017
comment
Хотя этот код может ответить на вопрос, предоставление дополнительного контекста относительно как и почему он решает проблему, улучшило бы долгосрочную ценность ответа. - person Alexander; 27.03.2018
comment
Какое дополнение может быть полезным? может пример? - person Super Mario; 29.03.2018

Я исправляю ответ, и работы включают все строки (на основе ответа Павла Лекича)

    (
    SELECT a.* FROM tablea a
    LEFT JOIN tableb b ON a.`key` = b.key
    WHERE b.`key` is null
    )
    UNION ALL
    (
    SELECT a.* FROM tablea a
    LEFT JOIN tableb b ON a.`key` = b.key
    where  a.`key` = b.`key`
    )
    UNION ALL
    (
    SELECT b.* FROM tablea a
    right JOIN tableb b ON b.`key` = a.key
    WHERE a.`key` is null
    );
person Rubén Ruíz    schedule 22.05.2017
comment
Нет, это тип внешнего соединения, при котором будут возвращены только строки из tablea, не совпадающие с tableb, и наоборот. Вы пытаетесь UNION ALL, что будет работать только в том случае, если в этих двух таблицах будут столбцы с одинаковым порядком, что не гарантируется. - person Marc L.; 24.07.2017
comment
он работает, я создаю в базе данных temp tablea (1,2,3,4,5,6) и tableb (4,5,6,7,8,9) его строки имеют 3 идентификатора столбца, номер и name_number в виде текста, и работает только в результате (1,2,3,7,8,9) - person Rubén Ruíz; 28.07.2017
comment
Это не внешнее соединение. Внешнее соединение также включает совпадающие элементы. - person Marc L.; 28.07.2017
comment
это новое предложение имеет все результаты 1,2, ..., 9 - person Rubén Ruíz; 02.08.2017

Ответ:

SELECT * FROM t1 FULL OUTER JOIN t2 ON t1.id = t2.id;

Можно воссоздать следующим образом:

 SELECT t1.*, t2.* 
 FROM (SELECT * FROM t1 UNION SELECT name FROM t2) tmp
 LEFT JOIN t1 ON t1.id = tmp.id
 LEFT JOIN t2 ON t2.id = tmp.id;

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

Объяснение:

Есть крайний случай, который UNION или UNION ALL не может охватить. Мы не можем протестировать это на mysql, поскольку он не поддерживает ПОЛНЫЕ ВНЕШНИЕ СОЕДИНЕНИЯ, но мы можем проиллюстрировать это на базе данных, которая его поддерживает:

 WITH cte_t1 AS
 (
       SELECT 1 AS id1
       UNION ALL SELECT 2
       UNION ALL SELECT 5
       UNION ALL SELECT 6
       UNION ALL SELECT 6
 ),
cte_t2 AS
(
      SELECT 3 AS id2
      UNION ALL SELECT 4
      UNION ALL SELECT 5
      UNION ALL SELECT 6
      UNION ALL SELECT 6
)
SELECT  *  FROM  cte_t1 t1 FULL OUTER JOIN cte_t2 t2 ON t1.id1 = t2.id2;

This gives us this answer:

id1  id2
1  NULL
2  NULL
NULL  3
NULL  4
5  5
6  6
6  6
6  6
6  6

Решение UNION:

SELECT  * FROM  cte_t1 t1 LEFT OUTER JOIN cte_t2 t2 ON t1.id1 = t2.id2
UNION    
SELECT  * FROM cte_t1 t1 RIGHT OUTER JOIN cte_t2 t2 ON t1.id1 = t2.id2

Дает неверный ответ:

 id1  id2
NULL  3
NULL  4
1  NULL
2  NULL
5  5
6  6

Решение UNION ALL:

SELECT  * FROM cte_t1 t1 LEFT OUTER join cte_t2 t2 ON t1.id1 = t2.id2
UNION ALL
SELECT  * FROM  cte_t1 t1 RIGHT OUTER JOIN cte_t2 t2 ON t1.id1 = t2.id2

Тоже неверно.

id1  id2
1  NULL
2  NULL
5  5
6  6
6  6
6  6
6  6
NULL  3
NULL  4
5  5
6  6
6  6
6  6
6  6

Тогда как этот запрос:

SELECT t1.*, t2.*
FROM (SELECT * FROM t1 UNION SELECT name FROM t2) tmp 
LEFT JOIN t1 ON t1.id = tmp.id 
LEFT JOIN t2 ON t2.id = tmp.id;

Дает следующее:

id1  id2
1  NULL
2  NULL
NULL  3
NULL  4
5  5
6  6
6  6
6  6
6  6

Порядок другой, но в остальном соответствует правильному ответу.

person Angelos    schedule 09.02.2017
comment
Это мило, но искажает UNION ALL решение. Кроме того, в нем представлено решение, использующее UNION, которое будет медленнее для больших исходных таблиц из-за требуемой дедупликации. Наконец, он не будет компилироваться, потому что поле id не существует в подзапросе tmp. - person Marc L.; 24.07.2017
comment
Я никогда не заявлял о скорости, и в OP ничего не говорилось о скорости. Предполагая, что UNION ALL (вы не указываете, какой именно), и оба они дают правильный ответ, если бы мы хотели сделать утверждение, что один из них быстрее, нам нужно было бы предоставить тесты, и это было бы отклонением от OP вопрос. - person Angelos; 25.07.2017
comment
Что касается замечания об отсутствии идентификатора в подзапросе, я исправил опечатку - спасибо, что указали на нее. Ваше заявление об искажении фактов является расплывчатым - если, возможно, вы могли бы предоставить дополнительную информацию, я могу ответить на этот вопрос. Что касается вашего последнего наблюдения о привлекательности, у меня нет комментариев, я бы предпочел сосредоточиться на логике sql. - person Angelos; 25.07.2017
comment
Искажает: UNION ALL решение: ... Также неверно. Код, который вы представляете, не учитывает исключение пересечения из правого соединения (where t1.id1 is null), которое должно быть указано в UNION ALL. Другими словами, ваше решение превосходит все остальные, только если одно из этих решений реализовано неправильно. Что касается привлекательности, то внимание было уделено. Мои извинения, это было безвозмездно. - person Marc L.; 25.07.2017

Это также возможно, но вы должны указать те же имена полей в select.

SELECT t1.name, t2.name FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION
SELECT t1.name, t2.name FROM t2
LEFT JOIN t1 ON t1.id = t2.id
person alamelu    schedule 03.02.2014
comment
Это просто дублирование результатов левого соединения. - person Matthew Read; 27.03.2018
comment
Это не даст результата, эквивалентного полному внешнему соединению. - person Ankit Jindal; 05.09.2020

Стандарт SQL говорит, что full join on - это inner join on строки, union all несопоставленные строки левой таблицы, расширенные нулями, union all строки правой таблицы, расширенные нулями. Т.е. inner join on строк union all строк в left join on, но не inner join on union all строк в right join on, но не inner join on.

Т.е. left join on строк, union all right join on строк не в inner join on. Или, если вы знаете, что ваш inner join on результат не может иметь значение NULL в конкретном правом столбце таблицы, тогда «right join on строк не в inner join on» - это строки в right join on с условием on, расширенным на and этот столбец is null.

Т.е. аналогично right join on union all соответствующие left join on строки.

Из В чем разница между «INNER JOIN» и «OUTER JOIN»?:

(Стандарт SQL 2006, правила синтаксиса SQL / Foundation 7.7 1, общие правила 1 b, 3 c и d, 5 b.)

person philipxy    schedule 11.08.2018