T-SQL - левые внешние соединения - фильтры в предложении where по сравнению с предложением on - PullRequest
6 голосов
/ 28 мая 2010

Я пытаюсь сравнить две таблицы, чтобы найти строки в каждой таблице, которых нет в другой. Таблица 1 имеет столбец groupby для создания 2 наборов данных в первой таблице.

groupby     number
----------- -----------
1           1
1           2
2           1
2           2
2           4

В таблице 2 есть только один столбец.

number
-----------
1
3
4

Таким образом, таблица 1 имеет значения 1,2,4 в группе 2, а таблица 2 имеет значения 1,3,4.

Ожидаю следующий результат при присоединении к Группе 2:

`Table 1 LEFT OUTER Join Table 2`
T1_Groupby  T1_Number   T2_Number
----------- ----------- -----------
2           2           NULL

`Table 2 LEFT OUTER Join Table 1`
T1_Groupby  T1_Number   T2_Number
----------- ----------- -----------
NULL        NULL        3

Единственный способ заставить это работать, если я добавлю предложение where для первого соединения:

PRINT 'Table 1 LEFT OUTER Join Table 2, with WHERE clause'
select  table1.groupby as [T1_Groupby],
        table1.number as [T1_Number],
        table2.number as [T2_Number]
from    table1
        LEFT OUTER join table2
        --******************************
        on table1.number = table2.number
        --******************************
WHERE   table1.groupby = 2
    AND table2.number IS NULL

и фильтр во втором:

PRINT 'Table 2 LEFT OUTER Join Table 1, with ON clause'
select  table1.groupby as [T1_Groupby],
        table1.number as [T1_Number],
        table2.number as [T2_Number]
from    table2
        LEFT OUTER join table1
            --******************************
            on table2.number = table1.number
            AND table1.groupby = 2
            --******************************
WHERE   table1.number IS NULL

Может кто-нибудь придумать способ не использовать фильтр в предложении on, а в предложении where?

В контексте этого у меня есть промежуточная область в базе данных, и я хочу определить новые записи и записи, которые были удалены. Поле groupby является эквивалентом пакета для извлечения, и я сравниваю последний экстракт в временной таблице с пакетом из вчерашнего дня, хранящимся в таблице разделений, которая также содержит все ранее извлеченные пакеты. Код для создания таблицы 1 и 2:

create table table1 (number int, groupby int)
create table table2 (number int)
insert into table1 (number, groupby) values (1, 1)
insert into table1 (number, groupby) values (2, 1)
insert into table1 (number, groupby) values (1, 2)
insert into table2 (number) values (1)
insert into table1 (number, groupby) values (2, 2)
insert into table2 (number) values (3)  
insert into table1 (number, groupby) values (4, 2)  
insert into table2 (number) values (4)  

EDIT:

Чуть больше контекста - в зависимости от того, куда я положил фильтр, я получаю разные результаты. Как указывалось выше, предложение where дает мне правильный результат в одном состоянии и ON в другом. Я ищу последовательный способ сделать это.

Где -

select  table1.groupby as [T1_Groupby],
        table1.number as [T1_Number],
        table2.number as [T2_Number]
from    table1
        LEFT OUTER join table2
            --******************************
            on table1.number = table2.number
            --******************************
WHERE   table1.groupby = 2 
    AND table2.number IS NULL

Результат:

T1_Groupby  T1_Number   T2_Number
----------- ----------- -----------
2           2           NULL

Вкл. -

select  table1.groupby as [T1_Groupby],
        table1.number as [T1_Number],
        table2.number as [T2_Number]
from    table1
        LEFT OUTER join table2
            --******************************
            on table1.number = table2.number
            AND table1.groupby = 2
            --******************************
WHERE   table2.number IS NULL

Результат:

T1_Groupby  T1_Number   T2_Number
----------- ----------- -----------
1           1           NULL
2           2           NULL
1           2           NULL

Где (на этот раз таблица 2) -

select  table1.groupby as [T1_Groupby],
        table1.number as [T1_Number],
        table2.number as [T2_Number]
from    table2
        LEFT OUTER join table1
            --******************************
            on table2.number = table1.number
            AND table1.groupby = 2
            --******************************
WHERE   table1.number IS NULL

Результат:

T1_Groupby  T1_Number   T2_Number
----------- ----------- -----------
NULL        NULL        3

Вкл. -

select  table1.groupby as [T1_Groupby],
        table1.number as [T1_Number],
        table2.number as [T2_Number]
from    table2
        LEFT OUTER join table1
            --******************************
            on table2.number = table1.number
            --******************************
WHERE   table1.number IS NULL
    AND table1.groupby = 2

Результат:

T1_Groupby  T1_Number   T2_Number
----------- ----------- -----------
(0) rows returned

Ответы [ 6 ]

12 голосов
/ 28 мая 2010

Если вы отфильтруете левую внешнюю объединенную таблицу в предложении WHERE, то вы фактически создаете внутреннее объединение

См. Также эту вики-страницу: ГДЕ условия ЛЕВОГО СОЕДИНЕНИЯ

5 голосов
/ 28 мая 2010

с LEFT OUTER JOINS, вы должны отфильтровать предложение ON или использовать это:

WHERE
    (LeftJoinTable.ID IS NULL OR LeftJoinTable.Col1=YourFilter)

, если вы просто отфильтруете WHERE:

WHERE 
    LeftJoinTable.Col1=YourFilter

вы откажетесь от родителяприсоединение к строке всякий раз, когда нет LeftJoinTable.ID (что делает присоединение INNER JOIN).

Помещая фильтр в ON, вы вызываете исключение строки LEFT JOIN, но не исключение родительскогоприсоединение к строке, вот как это работает.

EDIT base don Комментарий OP
единственный способ отфильтровать таблицу LEFT OUTER JOIN - это предложение ON, если вы не хотитеиспользуйте ИЛИ, как я показал в первом примере кода выше.Нет ничего плохого в том, чтобы фильтровать LEFT OUTER JOIN в предложении ON, вот как вы это делаете.

2 голосов
/ 28 мая 2010

При написании запроса имеет смысл поместить соединение в предложение ON, так как вы специально хотите объединять только значения в группе '2' из таблицы 1.

Альтернативой является предварительная фильтрация таблицы1 в интересующей вас группе, например

select  t1Group.groupby,
        t1Group.number as [T1_Number],
        table2.number as [T2_Number]
from    table2
        LEFT OUTER join (SELECT * FROM table1 WHERE groupby=2) t1Group
            on table2.number = t1Group.number
WHERE   t1Group.number IS NULL
0 голосов
/ 25 июля 2014

Я сам боролся с этим - и в конце дня я должен был выбрать данные из таблицы с помощью Where Clause и поместить их во временную таблицу, а затем использовать левое внешнее соединение для временной таблицы.

SELECT table1.GroupBy, table1.number INTO #Temp FROM table1 WHere GroupBy = 2
SELECT table2.Groupby, #temp.number From table2 LEFT OUTER JOIN #temp on table2.Groupby = #temp.Groupby
0 голосов
/ 17 июля 2013
    select  dbo.table1.groupby as [T1_Groupby],
                            dbo.table1.number as [T1_Number],
                            t22.number as [t22_Number]

                    from    dbo.table1 right outer join 
                    (select  dbo.table1.groupby,
                            dbo.table2.number as number

                    from    dbo.table1
                    right OUTER join dbo.table2
                    on dbo.table1.number = dbo.table2.number

                    where dbo.table1.number is null) t22
                    on dbo.table1.groupby = t22.number
                    where dbo.table1.groupby is null
0 голосов
/ 17 июля 2013
SELECT  dbo.table1.groupby as [T1_Groupby],
        dbo.table1.number as [T1_Number],
        t21.number as [t21_Number]
FROM    dbo.table1
LEFT OUTER join dbo.table2 t21
    ON dbo.table1.number = t21.number
LEFT OUTER join dbo.table2 t22
    ON dbo.table1.groupby= t22.number
WHERE t21.number is null AND t22.number is null
...