2010-12-09 5 views
0

я рву на себе волосы по этому поводу:Проблема добавления предикаты внешнего соединения

-- misses nulls 
SELECT * 
FROM BASE_TABLE TAB1 
    FULL JOIN BASE_TABLE TAB2 
     USING (ANOTHER_ID) 
WHERE (TAB1.ID = 6 OR TAB1.ID IS NULL) 
    AND (TAB2.ID = 8 OR TAB2.ID IS NULL); 

-- catches nulls 
SELECT * 
FROM (SELECT * FROM BASE_TABLE WHERE ID = 6) TAB1 
    FULL JOIN (SELECT * FROM BASE_TABLE WHERE ID = 8) TAB2 
     USING (ANOTHER_ID); 

Первый запрос теряет строки, где строка в одной или другой таблицы не существует. Почему первый запрос не выполняет внешнее соединение?

Я продолжаю думать, что у меня есть это - предложение WHERE оценивается первым, поэтому «OR IS NULL» не применяется позже, но это не имеет смысла для меня, потому что я успешно применил «IS NULL 'предикаты в прошлом для выбора строк после объединения.

Я хотел бы сделать первый запрос работы по соображениям производительности - кто-нибудь знает, в чем проблема?

+0

Я смущен о `USING` - какой синтаксис это? Я не могу найти его в каких-либо документах, обычный SQL использует `ON TAB1.ANOTHER_ID = TAB2.ANOTHER_ID` ..? – thomaspaulb 2010-12-09 23:25:21

+0

@littlegreen Функция Oracle с 9i. Это существенно сокращает то, что вы написали. – 2010-12-09 23:27:11

+2

@littlegreen, djacobson: при использовании `USING (ANOTHER_ID)` он приводит только к одному столбцу с именем ANOTHER_ID (на который нельзя ссылаться по псевдониму), тогда как синтаксис `ON` приводит к обеим столбцам. – 2010-12-10 02:34:26

ответ

3

Первый запрос выполняет объединение, затем фильтр, второй - фильтр, затем соединение. С внешним соединением важное значение имеет различие.

Вы поймете это с некоторыми данными примера.

create table tab1 (id number, another_id number); 
create table tab2 (id number, another_id number); 

insert into tab1 values (6,5); 
insert into tab2 values (8,5); 
insert into tab1 values (1,6); 
insert into tab2 values (2,6); 

SELECT * 
FROM TAB1 
    FULL JOIN TAB2 USING (ANOTHER_ID); 

    ANOTHER_ID    ID    ID 
--------------- --------------- --------------- 
      5.00   6.00   8.00 
      6.00   1.00   2.00 

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

Когда вы добавляете фильтр WHERE, он отфильтровывает соответствие для 6, так как идентификаторы не являются ни 6,8, ни нулями. То есть вы использовали его как предикат фильтра, а не как предикат соединения.

Я считаю, что вы планировали, что для TAB1, чтобы присоединиться к TAB2, должно быть соответствие ANOTHER_ID плюс идентификатор должен быть 6 для TAB1 и 8 для TAB2. Это то, что находится во втором SQL.

предиката также может быть выражено как

SELECT * 
FROM TAB1 
    FULL JOIN TAB2 ON 
      (TAB1.ANOTHER_ID = TAB2.ANOTHER_ID AND TAB1.ID=6 AND TAB2.ID=8) 
0

Hm. Это болванка, но я думаю, что у меня это получилось.

В своем первом запросе вы полностью присоединяетесь к таблице на ключ ANOTHER_ID.

Когда две объединенные таблицы одинаковы, неважно, выполняете ли вы полное соединение, внутреннее соединение, левое соединение или правое соединение: результат тот же. Поскольку ваш ключ ANOTHER_ID всегда либо существует, либо не существует в обеих таблицах. Нет случаев, когда одна таблица имеет значение для ANOTHER_ID, которое не встречается в другой таблице, и как таковое отсутствуют случаи, когда TAB1.ANOTHER_ID или TAB2.ANOTHER_ID заканчиваются NULL. Таким образом, вы на самом деле просто выполняете самостоятельное соединение с ANOTHER_ID.

Теперь я не знаю содержание вашего столбца идентификатора, но я предполагаю, что он всегда заполнен некоторой ценностью. Итак, после вашего самостоятельного соединения, итоговые столбцы ID всегда будут заполнены чем-то. Возможно, не 6 или 8, но и не NULL. При отсутствии значений NULL ваш запрос WHERE переводится в WHERE TAB1.ID = 6 AND TAB2.ID = 8, который оставляет только правильные комбинации и ничего больше.

Напротив, во втором запросе вы определяете подмножества ID = 6 и ID = 8 и полностью соединяете эти подмножества друг с другом. Подмножество 1 содержит некоторые вхождения ANOTHER_ID, отсутствующие в подмножестве 2, и наоборот. Итак, теперь есть основание для ПОЛНОГО СОЕДИНЕНИЯ, потому что некоторые строки не будут соединены с другими строками, оставив значения NULL в TAB1.ID или TAB2.ID, которые вы можете обнаружить.

Я думаю, что ваш первый запрос можно изменить, изменив предложение WHERE на: WHERE TAB1.ID IN (6,8) AND TAB2.ID IN (6,8). Это, тем не менее, даст другой результат в качестве второго запроса, и я думаю, что и определенное количество повторяющихся строк. И я тоже не думаю, что это будет быстрее.

Смежные вопросы