Как сделать полнофункциональную работу в MySQL?
Я хочу сделать полное внешнее соединение в MySQL. Это возможно? Является ли Full Outer Join поддерживаемым MySQL?
Ответы
Ответ 1
У вас нет ПОЛНЫХ СОЕДИНЕНИЙ на 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 не приводит к появлению повторяющихся строк. Приведенный выше запрос зависит от оператора set UNION
для удаления дублирующихся строк, введенных шаблоном запроса. Мы можем избежать появления повторяющихся строк, используя шаблон против объединения для второго запроса, а затем использовать оператор множеств 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
Ответ 2
Ответ, который Pablo Santa Cruz дал правильно; однако, если кто-нибудь наткнулся на эту страницу и хочет получить больше разъяснений, вот подробный анализ.
Примеры таблиц
Предположим, что мы имеем следующие таблицы:
-- 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
Right Outer Join
Прямое внешнее соединение, например:
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
как о значении "запустить оба этих запроса, а затем складывать результаты друг над другом"; некоторые из строк будут получены из первого запроса, а некоторые из второго.
Следует отметить, что 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
.
Ответ 3
Использование запроса 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
with 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
визуализации 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]
Ответ 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
Ответ 5
Ни один из приведенных выше ответов не является правильным, потому что они не следуют семантике при наличии дублированных значений.
Для запроса, такого как (из этого duplicate):
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
-серверный оператор сравнения <=>
, а не =
.
Ответ 6
MySql не имеет синтаксиса FULL-OUTER-JOIN. Вы должны подражать, выполнив обе функции: ЛЕВЫЙ и ПРАВЫЙ РЕЙТИНГ как follows-
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 в предложениях 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'
получает перевод к следующему запросу Optimizer-
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. Таким образом, ответственный разработчик должен обращать внимание на эти нюансы при создании запроса.
Ответ 7
Измененный запрос 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
Ответ 8
Вы можете сделать следующее:
(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);
Ответ 9
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
Ответ 10
Mysql как таковой не поддерживает команду FULL OUTER JOIN. Три поддерживаемых соединения - INNER JOIN, LEFT JOIN и RIGHT JOIN.
Однако вы можете реализовать полное внешнее соединение, используя команду UNION as
(запрос левого соединения) UNION (запрос прямого соединения)
Например, рассмотрим следующий пример, в котором у меня есть два студента и метки таблиц. Чтобы выполнить полное внешнее соединение, я бы выполнил следующий код:
SELECT * FROM students
LEFT JOIN marks
ON students.id = marks.id
UNION ALL
SELECT * FROM students
RIGHT JOIN marks
ON students.id = marks.id;
Ответ 11
что вы сказали о решении Cross join?
SELECT t1.*, t2.*
FROM table1 t1
INNER JOIN table2 t2
ON 1=1;
Ответ 12
среди прочего, можно легко реализовать full outer join
используя cross join
, union
с нулевой строкой и предложение where
, например так:
SELECT left.a_left, left.b, right.a_right, right.c, right.d
FROM (
SELECT a_left, b
FROM left_table
UNION
SELECT NULL, NULL
) AS left
CROSS JOIN
(
SELECT a_right, c, d
FROM right_table
UNION
SELECT NULL, NULL, NULL
) AS right
WHERE (left.a_left = right.a_right
OR left.a_left IS NULL
OR right.a_right IS NULL)
AND NOT (left.a_left IS NULL AND right.a_right IS NULL)
не знаю о производительности, но этот метод очень легко понять, ИМО
Ответ 13
Это также возможно, но вы должны указать те же имена полей в 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
Ответ 14
Я исправляю ответ, и работа включает все строки (на основе ответа Павле Лекича)
(
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
);
Ответ 15
Ответ:
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 JOINs, но мы можем проиллюстрировать это в базе данных, которая его поддерживает:
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
Порядок отличается, но в противном случае соответствует правильному ответу.
Ответ 16
Стандарт SQL говорит full join on
это inner join on
строках union all
непарных левых строк таблиц продолженных NULLS union all
правые строки таблицы протяженных по нулям. Т.е. inner join on
строках inner join on
union all
строки в left join on
но не inner join on
union all
строки в right join on
но не inner join on
.
Т.е. left join on
union all
строк 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.)