SQL: где и против в соединении - PullRequest
3 голосов
/ 13 апреля 2010

Возможно, глупый вопрос, но рассмотрим эти 2 таблицы:

T1                       
Store    Year
01  2009
02  2009
03  2009
01  2010
02  2010
03  2010

T2
Store
02

Почему это ВНУТРЕННЕЕ СОЕДИНЕНИЕ дает мне желаемые результаты (фильтруя [год] в предложении ON):

select t1.*
from t1
inner join t2
on t1.store = t2.store
and t1.[year] = '2009'

Store    Year
02  2009

А почему в левое внешнее соединение включены записи 2010 года?

select t1.*
from t1
left outer join t2
on t1.store = t2.store
and t1.year = '2009'
where t2.store is null

01  2009
03  2009
01  2010
02  2010
03  2010

И я должен написать фильтр [year] в предложении WHERE:

select t1.*
from t1
left outer join t2
on t1.store = t2.store
where t2.store is null
and t1.year = '2009'

01  2009
03  2009

Как я уже сказал, возможно, глупый вопрос, но он меня беспокоит!

Ответы [ 2 ]

3 голосов
/ 13 апреля 2010

Если вы определяете ЛЕВОЕ СОЕДИНЕНИЕ, что

  • для каждой строки из левой таблицы будет возвращаться соответствующая строка из правой таблицы, если она существует
  • если строк с правой стороны не существует, он все равно будет возвращать строку из левой таблицы со всеми столбцами из правой таблицы, установленными в NULL

Теперь поймите, что существующее эквивалентно условию ON, оценивается как истинное, и все это имеет смысл.

Для строки, где t1.year равен 2010, выражение on оценивается как false (x AND 2010 = 2009 - false для всех строк, где t1.year = 2010), но поскольку это левое соединение, строка из левой таблицы будет все еще будет возвращено (согласно определению).

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

(в общем случае это не таблица, а выражение выбора)

EDIT: Как интересно заметил Эрвин, условие where можно превратить в JOIN с помощью

select t1.*
from t1
left outer join t2
on t1.store = t2.store
or t1.year = '2009'
where t2.store is null

потому что:

t1.store t1.year t2.store t1.store=t2.store t1.year=2009  join(OR)
01       2009    02       false             true          true
02       2009    02       true              true          true
03       2009    02       false             true          true
01       2010    02       false             false         false
02       2010    02       true              false         true
03       2010    02       false             false         false

, поэтому только строки, в которых столбец соединения в приведенной выше таблице равен false , будут возвращать нули в полях t2. *.

Что я хотел сказать, так это то, что такого рода условие нельзя превратить в чистое соединение , потому что способ левого соединения работает (по-прежнему возвращает записи, даже если условие соединения ложно), что возможно при условия внутреннего соединения (могут быть превращены в чистые соединения и наоборот).

Относительно предложенного запроса - мой совет - не использовать его.

Запросы с OR, как правило, выполняются хуже, чем запросы с условиями AND (OR расширяет набор результатов; ANDs ограничивают). Это относится как к условиям присоединения, так и к условиям условия.

Ваш запрос будет работать хуже, чем запрос с t1.year = '2009' в условии where, потому что в более позднем случае будет возможно использовать индекс (если он существует), потому что, если вы присоединитесь, как вы, вы в основном искусственно объединяете записи из одной таблицы с записями из другой только для того, чтобы при условии вашего условия фильтровать только те записи, которые вам нужны. Должно быть более эффективным получать только записи с t1, начиная с 2009 года (при условии, что существует годовой индекс и достаточно высокая селективность, что будет с условием WHERE).

Оба эти предложения относительно производительности могут и должны быть проверены путем проверки плана запроса.

Наконец, запрос немного неясен - не сразу очевидно, что в случае t1.year = '2009' объединение становится декартовым произведением (которое будет отфильтровано позже). Таким образом, если предположение о том, что простые LEFT JOIN с where t1.year = 2009 AND t2.store is null работают лучше И более читабельно, я бы не использовал этот запрос.

0 голосов
/ 13 апреля 2010

Ваш второй запрос возвращает все результаты из первой таблицы, кроме тех, которые совпадают в соединении (результаты которого можно увидеть в первом запросе.) Это может быть проще для вас, если вы уберете "где t2 .store имеет значение null и добавляет «t2.Store» в выбранные столбцы. Результаты, которые вы должны увидеть:

01 2009 null
02 2009 02
03 2009 null
01 2010 null
02 2010 null
03 2010 null

Как вы можете видеть, если вы затем фильтруете только те строки, где t2.store имеет значение null, все, что вы делаете, это вычитаете вторую строку (единственную, которая соответствует предложению соединения).

...