Не получает все записи из таблицы A в левом соединении

#mysql #sql #join #left-join

#mysql #sql #Присоединиться #левое соединение

Вопрос:

У меня есть таблица статусов проверки качества для рецептов, и я хочу выбрать все записи «отбросить», включая информацию из «идеальных» статусов, где они перекрываются. Однако я получаю только пересечение, и я хочу, чтобы все записи «отбрасывались».

Я хочу выполнить левое соединение, которое даст мне все «отбрасываемые» строки из таблицы recipe_qa, объединенные с любыми «идеальными» строками.

 select *
from recipe_qa as bad
left join recipe_qa as good on good.id = bad.id
where bad.type = 'discard'
and good.type = 'perfect'
 

Приведенный выше запрос возвращает строки, в которых есть «идеальная» и «отброшенная» запись (24 строки) для идентификатора. Что я хочу, так это запрос, который выдаст мне все строки ‘discard’ (76 строк) либо с ‘perfect’ id, либо с null, где нет соответствующей строки.

Вот скрипка: http://sqlfiddle.com /#!2/faa49/4

Что я делаю не так?

Комментарии:

1. and (good.type = 'perfect' OR good.type is null)

Ответ №1:

Проще говоря, ваше предложение where устраняет их.

При работе с левыми (или внешними) соединениями вы должны учитывать, когда накладывается ограничение на данные, а когда создается декартово.

Допустим, вам нужны все записи из одной таблицы (A) и только те, которые совпадают в другой (B). При выполнении соединения в B будут присутствовать нулевые значения (не совпадающие записи с A). Добавление ограничивающего критерия (предложения where) к полям B фактически приведет к удалению записей, которые вы хотите получить из A; поскольку предложение where выполняется ПОСЛЕ объединения. Это имеет тот же эффект, как если бы вы начали с ВНУТРЕННЕГО СОЕДИНЕНИЯ! (в данном случае хорошо.type = ‘perfect’ удалит все те записи, которые являются плохими.type = ‘discard’, потому что когда good.id не существует для bad.id , хорошо.тип будет нулевым, а не «идеальным»; таким образом, where устраняет такие записи)

Этой ситуации можно избежать, просто переместив ограничивающие критерии в таблице B в объединение при использовании внешних соединений. Таким образом, декартово значение генерируется ПОСЛЕ наложения ограничения, гарантирующего, что «Все записи» из таблицы A остаются всеми записями. Если не в соединении, как вы видели, ограничение вводится после удаления декартовых и, следовательно, нулевых значений, и вы больше не получаете «Все записи», поэтому ЛЕВОЕ соединение отрицается. Это как если бы вы выполняли ВНУТРЕННЕЕ соединение в первую очередь. Вот почему выполнение оператора OR для возврата нулей и значения также работает, но только если это столбец NOT NULL (или тип null не имеет семантического значения), как указывает LC в комментариях ниже.

В этом случае ваше предложение where хорошо.type устраняет результаты левого соединения, поэтому либо добавьте критерии к объединению, которые устанавливают ограничения до создания декартовой системы (позволяя существовать нулям)

 select *
from recipe_qa as bad
left join recipe_qa as good 
  on good.id = bad.id
 and good.type = 'perfect'
where bad.type = 'discard'
 

http://sqlfiddle.com /#!2/faa49/8/0

ИЛИ используйте условие is null, чтобы не исключать записи из левого соединения. Это сопряжено с некоторыми рисками, указанными в приведенных ниже комментариях.

 select *
from recipe_qa as bad
left join recipe_qa as good 
  on good.id = bad.id
where bad.type = 'discard'
 and (good.type = 'perfect' or good.type is null)
 

Комментарии:

1. Я собирался предложить первый вариант здесь, SQL fiddle здесь: sqlfiddle.com /#!2/faa49/7

2. Я бы лично рекомендовал отказаться от второго варианта, если recipe_qa.type только это не столбец NOT NULL (или type значение null не имеет семантического значения).

3. У меня возникли проблемы с пониманием этого бита: "you have to account for the null records that are in the "matching" but not all records table source. »

4. Да, это плохо сформулировано. Я обновляю его.