2010-12-03 15 views
6

Necesito sincronizar dos tablas. Vamos a suponer que las tablas contienen columnas siguientes:Oracle: Fast NOT IN para múltiples columnas

Table1: A, B, C, D 
Table2: A, B, C, E 

necesito encontrar muchas filas de la Tabla 1 que no hay entrada con sus correspondientes valores en (A, B, C) Tabla 2, a continuación, calcular E, F (D) y la actualización Tabla2.

Si necesito hacer una correspondencia, p. Una única, que iba a escribir la siguiente consulta:

SELECT * FROM Table1 WHERE A NOT IN (SELECT A FROM Table2) 

analógico varias columnas parece ser demasiado lento:

SELECT * FROM Table1 WHERE A NOT IN (SELECT A FROM Table2) 
         AND B NOT IN (SELECT B FROM Table2) 
         AND C NOT IN (SELECT C FROM Table2) 

¿Cuál es la mejor manera de escribir dicha consulta?

Respuesta

18

Si (a, b, c) NO son NULOS en ambas tablas, tanto NOT IN como NOT EXISTS probablemente (en las versiones que he probado) generarán el mismo plan de ejecución.

Si (a, b, c) se declaran como anulable, pero saben que las columnas son, de hecho, no es nulo, se puede engañar al optimizador a hacer el hash contra unen todos modos añadiendo "Y a no es null Y b no es nulo Y c no es nulo "para su consulta. (También puede tener que añadir un/* + HASH_AJ */pista en la consulta sub.)

Además, las consultas siguientes no son idénticos:

from table1 
where (a,b,c) not in (select a,b,c from table2) 

from table1 
where a not in(select a from table2) 
    and b not in(select b from table2) 
    and c not in(select c from table2) 
5
SELECT * FROM Table1 
    WHERE (A, B, C) NOT IN 
    (SELECT A,B,C FROM Table2) 
+0

Esta es una solución menos que óptima. – erbsock

+1

que sería la mejor solución, aunque las columnas a, b, c no deben tener valores NULL (si hay valores NULL, la respuesta será incorrecta). @erbsock: si las columnas se declaran NOT NULL, el optimizador podrá usar las rutas más eficientes (ANTI-HASH JOIN ...) –

+0

@Vincent: ¿está asumiendo 11g? – erbsock

0

Usted puede tratar

SELECT * FROM Table1 
WHERE 
not exists (
    SELECT 1 FROM Table2 
    where Table2.a=Table1.a 
    and Table2.b=Table1.b 
    and Table2.c=Table1.c); 

tal como fue anunciado por guigui42. Hace un hash join anti y evita el filtro.

O tratar

select t1.* 
from table1 t1, table2 t2 
where t1.a = t2.a(+) 
and t1.b = t2.b(+) 
and t1.c = t2.c(+) 
and (t2.a is null or t2.b is null or t2.c is null); 

que hace una combinación externa + filtro. Ambas cosas deberían ser mucho más rápidas que hacer NOT IN.

0

Una pequeña adición: he encontrado que Oracle (11gR1 en mi caso) se niega a desmenuzar contra unirse cuando el nO eN cláusula contiene más de una columna, por ejemplo,

SELECT * FROM Table1 WHERE (A,B,C) NOT IN (
    SELECT /*+ HASH_AJ */ A,B,C FROM Table2 
     WHERE A IS NOT NULL AND B IS NOT NULL AND C IS NOT NULL 
) 

y esto incluso cuando la adición de uno de los consejos (lo mismo con UNNEST) y el no -NULL condiciones iones. Con solo una columna, funciona.

Cuestiones relacionadas