domingo, 18 de marzo de 2012

Trucos Oracle: Alternativa a NOT IN para evitar subconsultas

Problema: Necesitamos seleccionar un determinado valor (o varios) que NO pertenezca a un conjunto dado. Para esto, claro está, existe el operador de comparación NOT IN, que también puede usarse en subconsultas. Normalmente funciona muy bien, pero (especialmente en el caso de las subconsultas) puede resultar muuuy lento; dado que para saber si un elemento NO está en un conjunto, no queda más remedio que compararlo con TODOS los elementos del conjunto. Si el conjunto es muy grande y además queremos comprobar muchos elementos (caso muy típico en las subconsultas, p.e. buscar los elementos de una tabla que no están en otra) entonces la consulta puede ralentizarse enormemente.
Existe una alternativa, un tanto rebuscada, pero que reduce considerablemente el tiempo necesario de la consulta. Vamos a intentar explicarla mediante un ejemplo; buscamos en dos tablas, A y B, los registros de A que NO estén en B (tomando como base un campo determinado)

Normalmente haríamos:

/* Esto es usando NOT IN en un subquery */ 


select *
from A
where A.campo not in (select B.campo from B);

/* La alternativa sería: */

select A.*
from A,B
where A.campo = B.campo(+)
and B.campo is null;




Aquí estamos realizando una outer join (donde aparece el signo “+”), es decir, seleccionamos tanto los registros de A que se correspondan con uno de B, como los registros de A que NO se correspondan.


Luego seleccionamos los registros en los que el campo de B sea nulo (no confundirse, recordad que por la outer join estamos seleccionando registros de A), que son precisamente los de A de la outer join que no se corresponden con ninguno de B. 


El resultado es equivalente a los registros de A que NO están en B.
Adicionalmente, podemos seleccionar los registros de A que NO cumplen cierta condición (condición que se encuentra en la tabla B y por tanto allí deberemos comprobarla). De nuevo estamos en el caso del posible retardo al tratar de comprobar una condición negativa, y la solución es análoga; basta con añadir la condición a comprobar, pero, ojo, de forma positiva:


Select A.*

from A,B
where A.id = B.idA(+)
and B.idA is null
and B.campo_cond(+) = valor;





Es decir, aunque la condición comprueba que sea igual a cierto valor, el resultado final será, paradójicamente, los registros de A que NO cumplen esa condición (inclusive aquellos que no tienen correspondencia con ninguno de B). En este caso el campo de conexión sería la clave de A. 

Una advertencia: para que esto funcione correctamente, es importante asegurarse de que efectivamente existen registros de A que se correspondan con otros de B y que CUMPLAN la condición, ya que si no los resultados serían incorrectos; recuerda que la consulta se basa en la negación de una condición inicial, si ésta no existe, este método no es válido.


Fuente aquí

No hay comentarios:

Publicar un comentario