2010-05-28 11 views
6

Estoy tratando de comparar dos tablas para encontrar filas en cada tabla que no están en la otra. La Tabla 1 tiene una columna groupby para crear 2 conjuntos de datos dentro de la tabla uno.T-SQL - Uniones exteriores a la izquierda - Filtros en la cláusula where versus la cláusula on

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

La Tabla 2 tiene solo una columna.

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

Así Tabla 1 tiene los valores 1,2,4 en el grupo 2 y en la Tabla 2 tiene los valores 1,3,4.

espero que el siguiente resultado cuando se unen para el Grupo 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 

La única manera que puedo conseguir que esto funcione es si pongo una cláusula donde por primera unirse a:

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 

y un filtro en ON para el segundo:

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 

¿Alguien puede encontrar una forma de no usar el filtro? en la cláusula on pero en la cláusula where

El contexto de esto es que tengo un área de ensayo en una base de datos y quiero identificar nuevos registros y registros que se han eliminado. El campo groupby es el equivalente de un batchid para un extracto y estoy comparando el último extracto en una tabla temporal con el lote de ayer almacenado en una tabla partioneds, que también tiene todos los lotes extraídos previamente. El código para crear la tabla 1 y 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:

Un poco más de contexto - dependiendo de donde pongo el filtro I resultados diferentes. Como se indicó anteriormente, la cláusula where me da el resultado correcto en un estado y el ON en el otro. Estoy buscando una manera consistente de hacer esto.

Dónde -

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 

Resultado:

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

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 
      AND table1.groupby = 2 
      --****************************** 
WHERE table2.number IS NULL 

Resultado:

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

Dónde (tabla 2 esta vez) -

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 

Resultado:

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

On -

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 

Resultado:

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

¿Por qué es importante mover la condición de la cláusula 'JOIN' a la cláusula' WHERE'? Poner predicados en 'JOIN' es algo normal cuando quieres este tipo de comportamiento. – Aaronaught

+0

Moverlo a la cláusula where no es tan importante como el hecho de que para la primera unión donde uní la tabla 1 a la tabla 2 obtengo un resultado diferente si coloco el filtro en "on" y no en "where". PRINT 'Tabla 1 LEFT OUTER JOIN Tabla 2, la cláusula WHERE' seleccionar \t table1.groupby como [T1_Groupby], \t \t table1.number como [T1_Number], \t \t table2.number como [T2_Number] de \t tabla1 \t \t combinación externa izquierda tabla2 \t \t \t - ****************************** \t \t \t en table1.number = table2.number \t \t \t Y table1.groupby = 2 \t \t \t - ****************************** DONDE \t --table1 .groupby = 2 AND \t table2.number IS NULL Da nulo solo –

+0

Bueno, ¿cuál quieres? Parece que la unión NULL hace lo que quieres decir. Esta es una forma estándar de hacer consultas como esta y generalmente es preferible a las alternativas de subconsulta. No veo ningún beneficio al intentar impulsar la condición en una cláusula WHERE. – bobince

Respuesta

11

Si filtra la tabla unida exterior izquierdo en la cláusula WHERE, entonces son, en efecto, la creación de una unión interna

Véase también esta página wiki: WHERE conditions on a LEFT JOIN

+0

Gracias por el enlace wiki. –

5

con IZQUIERDA combinaciones externas, debe filtrar en la cláusula ON o usar esto:

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

si sólo filtro en el DONDE:

WHERE 
    LeftJoinTable.Col1=YourFilter 

se descartará el padre de unirse a la fila siempre que no haya una LeftJoinTable.ID (haciendo la combinación interna JOIN).

Al colocar el filtro en ON, causa la eliminación de la fila LEFT JOIN pero no la eliminación de la fila de unión primaria, así es como funciona.

EDITAR base de comentario de don OP
la única forma de filtrar una tabla de una combinación externa izquierda es en la cláusula ON, a menos que desee utilizar un OR como muestro en el primer ejemplo de código anterior. No hay nada de malo filtrando una UNIÓN EXTREMA IZQUIERDA en la cláusula ON, así es como lo haces.

+0

Probablemente (en retrospectiva, la ciencia perfecta) debería haber puesto esto en la pregunta original, pero si miran mi comentario anterior, verán que al cambiar el filtro desde dónde hacia el primero, cualquiera de las combinaciones obtiene un resultado diferente. Estoy buscando una 'regla' al codificar las uniones externas izquierdas para que use cláusulas ON o where. –

2

A medida que la consulta está escrito, que tiene sentido poner la unión en la cláusula ON, ya que específicamente sólo desea unirse a los valores en el grupo '2' del cuadro 1.

la alternativa es prefiltro tabla1 al grupo le interesa, como este

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

+1 la clave para entender por qué el filtrado en la cláusula "ON" funciona es tomar el filtro en una vista en línea como esta. – araqnid

0
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 
0
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

he estado luchando con esto por mí mismo - y al final de la jornada fue para seleccionar datos de la tabla con cláusula Where y ponerlo en una tabla temporal, y luego usar externa izquierda en la temperatura Mesa.

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 
Cuestiones relacionadas