Проблема добавления предикатов к внешнему соединению - PullRequest
0 голосов
/ 10 декабря 2010

Я рвал на себе волосы из-за этого:

-- 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, поэтому «ИЛИ НУЛЬ» позже не применяется, но для меня это не имеет смысла, потому что я успешно применил «НЕТ» предикаты в прошлом для выбора строк после объединения.

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

Ответы [ 2 ]

3 голосов
/ 10 декабря 2010

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

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

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 голосов
/ 10 декабря 2010

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, и наоборот.Так что теперь есть основание для FULL JOIN, потому что некоторые строки не будут соединены с другими строками, оставляя значения NULL в TAB1.ID или TAB2.ID, которые вы можете обнаружить.

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

...