Я хочу сделать полное внешнее соединение в MySQL. Это возможно? Поддерживается ли полное внешнее соединение MySQL?
sql
mysql
join
outer-join
full-outer-join
спенсер
источник
источник
Ответы:
У вас нет ПОЛНЫХ СОЕДИНЕНИЙ на MySQL, но вы можете их эмулировать .
Для кода SAMPLE, транскрибированного из этого SO вопроса, у вас есть:
с двумя таблицами t1, t2:
Приведенный выше запрос работает в особых случаях, когда операция FULL OUTER JOIN не приводит к появлению повторяющихся строк. Приведенный выше запрос зависит от
UNION
оператора set для удаления повторяющихся строк, введенных шаблоном запроса. Мы можем избежать появления повторяющихся строк, используя шаблон против объединения для второго запроса, а затем использовать оператор множеств UNION ALL для объединения двух множеств. В более общем случае, когда FULL OUTER JOIN будет возвращать повторяющиеся строки, мы можем сделать это:источник
(SELECT ... FROM tbl1 LEFT JOIN tbl2 ...) UNION ALL (SELECT ... FROM tbl1 RIGHT JOIN tbl2 ... WHERE tbl1.col IS NULL)
t1
иt2
, запрос в этом ответе действительно возвращает результирующий набор, который эмулирует FULL OUTER JOIN. Но в более общем случае, например, список SELECT не содержит достаточного количества столбцов / выражений, чтобы сделать возвращаемые строки уникальными, тогда этот шаблон запроса недостаточен для воспроизведения набора, который будет создан aFULL OUTER JOIN
. Чтобы получить более точную эмуляцию, нам понадобитсяUNION ALL
оператор set, а для одного из запросов потребуется шаблон анти-объединения . Комментарий Павле Лекича (выше) дает правильный шаблон запроса.Ответ, который дал Пабло Санта-Крус, правильный; однако, в случае, если кто-то наткнулся на эту страницу и хочет получить больше разъяснений, вот подробное описание.
Таблицы примеров
Предположим, у нас есть следующие таблицы:
Внутренние соединения
Внутреннее соединение, вот так:
Получат ли мы только записи, которые появляются в обеих таблицах, например так:
Внутренние объединения не имеют направления (например, влево или вправо), потому что они явно двунаправлены - нам требуется соответствие с обеих сторон.
Внешние соединения
Внешние объединения, с другой стороны, предназначены для поиска записей, которые могут не совпадать в другой таблице. Таким образом, вы должны указать, какой стороне соединения разрешено иметь отсутствующую запись.
LEFT JOIN
иRIGHT JOIN
стенография дляLEFT OUTER JOIN
иRIGHT OUTER JOIN
; Я буду использовать их полные имена ниже, чтобы усилить концепцию внешних объединений против внутренних объединений.Левое внешнее соединение
Левое внешнее соединение, вот так:
... получит все записи из левой таблицы независимо от того, совпадают ли они в правой таблице, например:
Правое внешнее соединение
Правильное внешнее соединение, вот так:
... получит все записи из правой таблицы независимо от того, совпадают ли они в левой таблице, например:
Полное внешнее соединение
Полное внешнее объединение даст нам все записи из обеих таблиц, независимо от того, имеют ли они совпадение в другой таблице, с NULL с обеих сторон, где нет совпадения. Результат будет выглядеть так:
Однако, как отметил Пабло Санта-Крус, MySQL не поддерживает это. Мы можем подражать ему, выполнив СОЮЗ левого соединения и правого соединения, например:
Вы можете думать о
UNION
значении «выполнить оба этих запроса, а затем поместить результаты друг на друга»; некоторые строки будут получены из первого запроса, а некоторые из второго.Следует отметить, что a
UNION
в MySQL устранит точные дубликаты: Тим будет появляться в обоих запросах здесь, но результатUNION
только перечисляет его один раз. Мой коллега по базе данных гуру считает, что на такое поведение нельзя полагаться. Чтобы быть более точным, мы могли бы добавитьWHERE
предложение ко второму запросу:С другой стороны, если вы хотите увидеть дубликаты по какой-то причине, вы можете использовать
UNION ALL
.источник
FULL OUTER JOIN
. Нет ничего плохого в том, чтобы выполнять запросы таким образом и использовать UNION для удаления этих дубликатов. Но чтобы по-настоящему реплицировать aFULL OUTER JOIN
, нам нужен один из запросов, чтобы быть анти-объединением.UNION
операция удалит эти дубликаты; но он также удаляет ВСЕ повторяющиеся строки, в том числе повторяющиеся строки, которые были бы в возвращенном FULL OUTER JOIN. Чтобы подражатьa FULL JOIN b
, правильный шаблон(a LEFT JOIN b) UNION ALL (b ANTI JOIN a)
.Использование
union
запроса удалит дубликаты, и это отличается от поведения, приfull outer join
котором никогда не удаляются дубликаты:Это ожидаемый результат
full outer join
:Это результат использования
left
иright Join
сunion
:[SQL Fiddle]
Мой предложенный запрос:
Результат вышеупомянутого запроса, который совпадает с ожидаемым результатом:
[SQL Fiddle]
Я решил добавить другое решение, основанное на
full outer join
визуализации и математике, оно не лучше, чем выше, но более читабельно:[SQL Fiddle]
источник
FULL OUTER JOIN
. Это сообщение в блоге также объясняет это хорошо - цитата из метода 2: «Это правильно обрабатывает дублирующиеся строки и не содержит ничего, что не должно. Необходимо использовать UNION ALL вместо простого UNION, что устранит дубликаты, которые я хочу сохранить. Это может быть значительно более эффективно для больших наборов результатов, так как нет необходимости сортировать и удалять дубликаты. "MySql не имеет синтаксиса FULL-OUTER-JOIN. Вы должны подражать, выполнив обе команды: «Влево» и «Вправо» следующим образом:
Но MySql также не имеет синтаксиса RIGHT JOIN. Согласно упрощению внешнего соединения MySql , правое соединение преобразуется в эквивалентное левое соединение путем переключения t1 и t2 в предложении
FROM
andON
в запросе. Таким образом, MySql Query Optimizer переводит исходный запрос в следующий:Теперь нет ничего плохого в том, чтобы написать исходный запрос как есть, но скажите, что если у вас есть предикаты, такие как предложение WHERE, которое является предикатом before-join или предикатом AND для
ON
предложения, которое является предикатом во время соединения , то вы возможно, захотите взглянуть на дьявола; который в деталях.Оптимизатор запросов MySql регулярно проверяет предикаты, если они отклонены . Теперь, если вы выполнили ПРАВИЛЬНОЕ СОЕДИНЕНИЕ, но с предикатом WHERE для столбца от t1, вы можете столкнуться с риском отклонения сценария с нулевым отклонением .
Например, следующий запрос -
Оптимизатор запросов переводит на
Таким образом, порядок таблиц изменился, но предикат все еще применяется к t1, но t1 теперь находится в предложении ON. Если t1.col1 определен как
NOT NULL
столбец, то этот запрос будет отклонен как ноль .Любое внешнее объединение (слева, справа, полное), отклоненное нулем , преобразуется во внутреннее соединение MySql.
Таким образом, ожидаемые результаты могут полностью отличаться от результатов MySql. Вы можете подумать, что это ошибка с правым соединением MySql, но это не так. Просто так работает оптимизатор запросов MySql. Таким образом, ответственный разработчик должен обращать внимание на эти нюансы при создании запроса.
источник
В SQLite вы должны сделать это:
источник
Ни один из приведенных выше ответов на самом деле не является правильным, поскольку они не следуют семантике при наличии дублированных значений.
Для запроса, такого как (из этого дубликата ):
Правильный эквивалент:
Если вам нужно, чтобы это работало со
NULL
значениями (что также может быть необходимо), используйте вместо этогоNULL
оператор сравнения -safe .<=>
=
источник
FULL OUTER JOIN
когдаname
столбец равен нулю.union all
Запрос с анти-Join шаблоном должен воспроизвести внешнее соединение поведения правильно, но какое решение является более подходящим , зависит от контекста и от ограничений, которые действуют на столах.union all
, но в этом ответе пропущен шаблон против объединения в первом или втором запросе, который сохранит существующие дубликаты, но предотвратит добавление новых. В зависимости от контекста другие решения (как этот) могут быть более подходящими.Модифицированный запрос shA.t для большей ясности:
источник
Вы можете сделать следующее:
источник
что вы сказали о решении Cross join ?
источник
select (select count(*) from t1) * (select count(*) from t2))
строками в наборе результатов.источник
Это также возможно, но вы должны упомянуть те же имена полей в select.
источник
Я фиксирую ответ, и работы включают все строки (на основе ответа Павла Лекича)
источник
tablea
которых нет совпадений,tableb
и наоборот. Вы пытаетесь это сделатьUNION ALL
, что будет работать, только если эти две таблицы имеют одинаково упорядоченные столбцы, что не гарантируется.Ответ:
Может быть воссоздано следующим образом:
Использование ответа UNION или UNION ALL не охватывает крайний случай, когда в базовых таблицах есть дублированные записи.
Объяснение:
Существует крайний случай, который UNION или UNION ALL не могут охватить. Мы не можем проверить это на mysql, так как он не поддерживает FULL OUTER JOIN, но мы можем проиллюстрировать это на базе данных, которая его поддерживает:
Решение UNION:
Дает неправильный ответ:
Решение UNION ALL:
Тоже неверно.
Тогда как этот запрос:
Дает следующее:
Порядок отличается, но в остальном совпадает с правильным ответом.
источник
UNION ALL
решение. Кроме того, он представляет решение,UNION
которое будет работать медленнее в больших исходных таблицах из-за необходимой дедупликации. Наконец, он не будет компилироваться, потому что полеid
не существует в подзапросеtmp
.UNION ALL
Решение: ... тоже неверно». Код, который вы представляете, исключает пересечение-исключение из функции right-join (where t1.id1 is null
), которая должна быть указана вUNION ALL
. То есть, ваше решение превосходит все остальные, только когда одно из этих решений неправильно реализовано. На "остроумие" точка взята. Это было безвозмездно, мои извинения.Стандарт 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»? :
источник