Как сделать FULL OUTER JOIN в MySQL?

654

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

спенсер
источник
2
возможный дубликат ошибки синтаксиса полного внешнего
Джо Стефанелли
4
На этот вопрос есть лучшие ответы
Хулио Маринс
Остерегайтесь ответов здесь. Стандарт SQL гласит, что полное соединение - это внутреннее соединение строк, объединяющее все несопоставленные строки левой таблицы, расширенные нулями, объединяет все строки правой таблицы, расширенные нулями. Большинство ответов здесь неправильные (см. Комментарии), а те, которые не ошибаются, не обрабатывают общий случай. Хотя есть много (неоправданных) голосов против. (См. Мой ответ.)
philipxy
Как насчет того, когда вы пытаетесь объединить не первичные ключи / сгруппированные столбцы? как у меня есть запрос продаж на уровне штата "штат", "продажи" и другой запрос расходов на состояние "штат", "расходы", оба запроса используют group by ("штат"). Когда я объединяю левые и правые объединения между двумя запросами, я получаю несколько строк с продажами, но без затрат, еще с расходами, но без продаж, все до этого момента, но я также получаю несколько с обоими продает и тратит, и повторяет колонку «состояние» ... не большая проблема, но не чувствует себя хорошо ...
Хайро Лосано
1
@JairoLozano Ограничения не нужны для запроса. Хотя, когда ограничения удерживаются, дополнительные запросы возвращают желаемый ответ, который иначе не был бы. Ограничения не влияют на то, какое полное соединение возвращается для данных аргументов. Проблема, которую вы описываете, заключается в том, что написанный вами запрос является неправильным. (Предположительно распространенная ошибка, когда люди хотят несколько объединений, каждое из которых, возможно, связано с другим ключом, некоторых подзапросов, каждое из которых возможно включает объединение и / или агрегацию, но они ошибочно пытаются выполнить все объединения, затем все агрегации или агрегировать по предыдущим агрегациям .)
Philipxy

Ответы:

669

У вас нет ПОЛНЫХ СОЕДИНЕНИЙ на MySQL, но вы можете их эмулировать .

Для кода SAMPLE, транскрибированного из этого 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 для удаления повторяющихся строк, введенных шаблоном запроса. Мы можем избежать появления повторяющихся строк, используя шаблон против объединения для второго запроса, а затем использовать оператор множеств UNION ALL для объединения двух множеств. В более общем случае, когда FULL OUTER JOIN будет возвращать повторяющиеся строки, мы можем сделать это:

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
Пабло Санта Круз
источник
33
На самом деле то, что вы написали, не правильно. Потому что когда вы делаете UNION, вы удаляете дубликаты, а иногда, когда вы объединяете две разные таблицы, должны быть дубликаты.
Павле Лекич
158
Это правильный пример:(SELECT ... FROM tbl1 LEFT JOIN tbl2 ...) UNION ALL (SELECT ... FROM tbl1 RIGHT JOIN tbl2 ... WHERE tbl1.col IS NULL)
Pavle Lekic
8
Таким образом, разница в том, что я делаю объединение слева и затем право справа, используя UNION ALL
Pavle Lekic
5
и теперь я вижу, что вы сами говорите это, извините. Возможно, вы могли бы обновить свой ответ, учитывая, что в этом случае он ошибается и что UNION ALL всегда будет более эффективным?
ysth
10
@ypercube: Если нет повторяющихся строк не в t1и t2, запрос в этом ответе действительно возвращает результирующий набор, который эмулирует FULL OUTER JOIN. Но в более общем случае, например, список SELECT не содержит достаточного количества столбцов / выражений, чтобы сделать возвращаемые строки уникальными, тогда этот шаблон запроса недостаточен для воспроизведения набора, который будет создан a FULL OUTER JOIN. Чтобы получить более точную эмуляцию, нам понадобится UNION ALLоператор set, а для одного из запросов потребуется шаблон анти-объединения . Комментарий Павле Лекича (выше) дает правильный шаблон запроса.
spencer7593
351

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

Таблицы примеров

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

-- 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 не поддерживает это. Мы можем подражать ему, выполнив СОЮЗ левого соединения и правого соединения, например:

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значении «выполнить оба этих запроса, а затем поместить результаты друг на друга»; некоторые строки будут получены из первого запроса, а некоторые из второго.

Следует отметить, что a 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.

Натан Лонг
источник
4
Для MySQL вы действительно хотите избегать использования UNION вместо UNION ALL, если нет перекрытия (см. Комментарий Павла выше). Если бы вы могли добавить больше информации об этом в своем ответе здесь, я думаю, что это был бы предпочтительный ответ на этот вопрос, поскольку он более тщательный.
Garen
2
Рекомендация «коллеги по работе с базами данных» верна. С точки зрения реляционной модели (вся теоретическая работа, проделанная Тедом Коддом и Крисом Дейтом), запрос последней формы эмулирует FULL OUTER JOIN, поскольку он объединяет два разных набора. Второй запрос не вводит «дубликаты» ( строки, уже возвращенные первым запросом), которые не были бы созданы FULL OUTER JOIN. Нет ничего плохого в том, чтобы выполнять запросы таким образом и использовать UNION для удаления этих дубликатов. Но чтобы по-настоящему реплицировать a FULL OUTER JOIN, нам нужен один из запросов, чтобы быть анти-объединением.
spencer7593
1
@IstiaqueAhmed: цель состоит в том, чтобы эмулировать операцию FULL OUTER JOIN. Нам нужно это условие во втором запросе, чтобы он возвращал только те строки, которые не совпадают (шаблон анти-объединения). Без этого условия запрос является внешним соединением ... он возвращает строки, которые совпадают, а также строки без соответствия. И соответствующие строки уже были возвращены первым запросом. Если второй запрос возвращает те же строки (снова), мы продублировали строки, и наш результат не будет эквивалентен FULL OUTER JOIN.
spencer7593
1
@IstiaqueAhmed: это правда, что UNIONоперация удалит эти дубликаты; но он также удаляет ВСЕ повторяющиеся строки, в том числе повторяющиеся строки, которые были бы в возвращенном FULL OUTER JOIN. Чтобы подражать a FULL JOIN b, правильный шаблон (a LEFT JOIN b) UNION ALL (b ANTI JOIN a).
spencer7593
1
Очень лаконичный ответ с отличным объяснением. Спасибо за это.
Наджиб
35

Использование 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 : [Из комментариев, большое спасибо!]
Примечание: Это может быть лучшим решением как для эффективности, так и для получения тех же результатов, что и 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]

shA.t
источник
Мы выполняем одно и то же время выполнения задачи. Если для t1 и t2 существуют подзапросы, то mysql должен выполнять одно и то же задание чаще, не так ли? Можем ли мы удалить это, используя псевдоним в этой ситуации?:
Кабир Хоссейн
Я предлагаю вам использовать несколько временных таблиц;).
shA.t
5
Этот метод представляется наилучшим решением как для эффективности, так и для получения тех же результатов, что и FULL OUTER JOIN. Это сообщение в блоге также объясняет это хорошо - цитата из метода 2: «Это правильно обрабатывает дублирующиеся строки и не содержит ничего, что не должно. Необходимо использовать UNION ALL вместо простого UNION, что устранит дубликаты, которые я хочу сохранить. Это может быть значительно более эффективно для больших наборов результатов, так как нет необходимости сортировать и удалять дубликаты. "
Стив Чемберс
2
@ SteveChambers слишком поздно, но спасибо за ваш комментарий. Я добавил ваш комментарий, чтобы потом ответить на выделенное больше. Если вы не согласны, пожалуйста, верните его обратно;).
shA.t
Нет проблем @ shA.t - IMO, это действительно должно иметь больше голосов и / или быть принятым ответом.
Стив Чемберс
6

MySql не имеет синтаксиса FULL-OUTER-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 , правое соединение преобразуется в эквивалентное левое соединение путем переключения t1 и t2 в предложении FROMand 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, но это не так. Просто так работает оптимизатор запросов MySql. Таким образом, ответственный разработчик должен обращать внимание на эти нюансы при создании запроса.

КОВЧЕГ
источник
4

В 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
Рами Джамле
источник
Можем ли мы использовать это? например, как: SELECT * FROM leftTable lt СЛЕДУЮЩЕЕ СОЕДИНЕНИЕ rightTable rt ON lt.id = rt.lrid UNION SELECT lt. *, rl. * - для сопоставления набора столбцов FROM leftTable lt RIGHT JOIN rightTable rt ON lt.id = rt.lrid ;
Кабир Хоссейн
да, но SQLite не поддерживает правильные объединения, но да, в MYSQL да
Рами Джамле
4

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

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

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оператор сравнения -safe .<=>=

Гордон Линофф
источник
3
часто это хорошее решение, но оно может давать результаты, отличные от значения, FULL OUTER JOINкогда nameстолбец равен нулю. union allЗапрос с анти-Join шаблоном должен воспроизвести внешнее соединение поведения правильно, но какое решение является более подходящим , зависит от контекста и от ограничений, которые действуют на столах.
Фтиелла
@fthiella. , , Это хороший момент. Я поправил ответ.
Гордон Линофф
1
хорошо, но оператор сравнения с нулевым значением сделает соединение успешным, что отличается от поведения полного внешнего соединения в случае, если у вас есть нулевые имена как в t1, так и в t2
fthiella
@fthiella. , , Я должен думать о лучшем способе сделать это. Но, учитывая, как ошибочно принятый ответ, почти все ближе к правильному ответу. (Этот ответ неверен, если с обеих сторон несколько клавиш.)
Гордон Линофф,
1
да, принятый ответ неверен, как общее решение, я думаю, что это правильно использовать union all, но в этом ответе пропущен шаблон против объединения в первом или втором запросе, который сохранит существующие дубликаты, но предотвратит добавление новых. В зависимости от контекста другие решения (как этот) могут быть более подходящими.
17
3

Модифицированный запрос 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 t2.value IS NULL 
a20
источник
3

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

(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);
Лоло
источник
1

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

SELECT t1.*, t2.*
FROM table1 t1
INNER JOIN table2 t2 
ON 1=1;
супер Марио
источник
2
Нет, это перекрестное соединение. Он будет сопоставлять каждую строку в t1 с каждой строкой в ​​t2, получая набор всех возможных комбинаций со select (select count(*) from t1) * (select count(*) from t2))строками в наборе результатов.
Марк Л.
Хотя этот код может ответить на вопрос, предоставление дополнительного контекста относительно того, как и почему он решает проблему, улучшит долгосрочную ценность ответа.
Александр
Какое дополнение может быть полезным? может быть, пример?
Super Mario
0
SELECT
    a.name,
    b.title
FROM
    author AS a
LEFT JOIN
    book AS b
    ON a.id = b.author_id
UNION
SELECT
    a.name,
    b.title
FROM
    author AS a
RIGHT JOIN
    book AS b
    ON a.id = b.author_id
Алекс Плютау
источник
0

Это также возможно, но вы должны упомянуть те же имена полей в 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
alamelu
источник
Это просто дублирование результатов левого соединения.
Мэтью Прочитал
-1

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

    (
    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
    );
Рубен Руис
источник
Нет, это тип соединения "only-only", которое будет возвращать только те строки, у tableaкоторых нет совпадений, tablebи наоборот. Вы пытаетесь это сделать UNION ALL, что будет работать, только если эти две таблицы имеют одинаково упорядоченные столбцы, что не гарантируется.
Марк Л.
это работает, я создаю на временной базе данных tablea (1,2,3,4,5,6) и tableb (4,5,6,7,8,9) его строки имеют 3 столбца "id", "number" и «name_number» как текст, и работы только в результате имеют (1,2,3,7,8,9)
Rubén Ruíz
1
Это не внешнее соединение. Внешнее объединение также включает совпадающие элементы.
Марк Л.
это новое предложение имеет все результаты 1,2, ..., 9
Рубен Руиз
-2

Ответ:

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, так как он не поддерживает FULL OUTER JOIN, но мы можем проиллюстрировать это на базе данных, которая его поддерживает:

 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

Порядок отличается, но в остальном совпадает с правильным ответом.

Ангелос
источник
Это мило, но искажает UNION ALLрешение. Кроме того, он представляет решение, UNIONкоторое будет работать медленнее в больших исходных таблицах из-за необходимой дедупликации. Наконец, он не будет компилироваться, потому что поле idне существует в подзапросе tmp.
Марк Л.
Я никогда не претендовал на скорость, и ОП не упоминал о скорости. Предполагая, что UNION ALL (вы не указываете, какой именно), и оба дают правильный ответ, если мы хотим сделать утверждение, что один из них быстрее, нам нужно предоставить эталонные тесты, и это будет отклонением от OP вопрос.
Ангелос
Что касается наблюдения за отсутствием идентификатора в подзапросе, я исправил опечатку - спасибо, что указали на это. Ваша претензия искажений искажена - если, возможно, вы могли бы предоставить больше информации, я могу решить эту проблему. На ваше последнее замечание о привлекательности, у меня нет никаких комментариев, я бы предпочел сосредоточиться на логике sql.
Ангелос
3
Искажает: « UNION ALLРешение: ... тоже неверно». Код, который вы представляете, исключает пересечение-исключение из функции right-join ( where t1.id1 is null), которая должна быть указана в UNION ALL. То есть, ваше решение превосходит все остальные, только когда одно из этих решений неправильно реализовано. На "остроумие" точка взята. Это было безвозмездно, мои извинения.
Марк Л.
-3

Стандарт 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 Standard 2006 SQL / Foundation 7.7 Синтаксические правила 1, Общие правила 1 b, 3 c & d, 5 b.)

philipxy
источник