2012-05-08 20 views
10

UNION y UNION ALL las consultas pueden superar las consultas equivalentes utilizando OR-predicados conectados en determinadas circunstancias. Que yo sepa, esto se debe en parte a que las subselecciones UNION se pueden ejecutar en paralelo y pueden tener su propio "subplano" específico para cada parte del predicado OR conectado, que probablemente sea mucho más óptimo debido a transformaciones de consulta aplicables más simples .Deje que Oracle convierta predicados conectados en OR en operaciones UNION ALL

Pero escribir OR predicados comunicado con los suele ser mucho más legible y conciso, incluso si la factorización subconsulta se aplicaron a una solución UNION ALL. Mi pregunta es: ¿hay alguna manera de indicarle a Oracle que un solo y costoso predicado OR-connected debería transformarse en una operación UNION ALL? Si existe tal sugerencia/método, ¿bajo qué circunstancias se puede aplicar (por ejemplo, si es necesario que existan restricciones en las columnas involucradas en los predicados, etc.)? Un ejemplo:

CREATE TABLE a AS 
    SELECT 1 x, 2 y FROM DUAL UNION ALL 
    SELECT 2 x, 1 y FROM DUAL; 

-- This query... 
SELECT * FROM a 
WHERE x = 1 OR y = 1 

-- Is sometimes outperformed by this one, for more complex table sources... 
-- Note: in my case, I can safely apply UNION ALL. I know the two predicates to 
-- be mutually exclusive. 
SELECT * FROM a 
WHERE x = 1 
UNION ALL 
SELECT * FROM a 
WHERE y = 1 

Nota, soy consciente de la /*+ USE_CONCAT */ pista:

SELECT /*+ USE_CONCAT */ * FROM a 
WHERE x = 1 OR y = 1 

Pero no parece producir lo que necesito (sin UNION ALL funcionamiento forzado en el plan de ejecución):

------------------------------------------- 
| Id | Operation   | Name | E-Rows | 
------------------------------------------- 
| 0 | SELECT STATEMENT |  |  | 
|* 1 | TABLE ACCESS FULL| A |  2 | 
------------------------------------------- 

Tal vez, hay alguna restricción a esta sugerencia? Tengo Oracle 11g2 disponible para esto.

+0

¿Cuántas filas (en% de todas las filas) devolverá la condición 'x = 1 oy = 1' (en la tabla real)? ¿Qué pasa con el uso de la sugerencia 'PARALELO' en la consulta" o "? –

+0

@a_horse_with_no_name: en realidad, la condición (real) es de la forma '(flag_function() = 1 y condition1) o (flag_function() = 0 y condition2)'. Las dos subcondiciones se excluyen mutuamente en función de un PL/SQL 'flag_function()'. Me he dado cuenta de que Oracle crea un plan mucho mejor para esto cuando se usa 'UNION ALL' en lugar de cuando se usa' OR'. 'PARALLEL' probablemente no ayude mucho, ya que la cantidad de datos no es tan grande en este caso, pero el plan es complejo ... Además, esta consulta se ejecuta con mucha frecuencia en las sesiones de los usuarios.No me gustaría ahogar demasiados recursos con las sugerencias 'PARALELAS' –

+1

Esas consultas no son equivalentes. Debería usar UNION en lugar de UNION ALL. Obtendrá resultados diferentes cuando tenga filas donde tanto x como y sean 1. – GriffeyDog

Respuesta

3

Creo que esto puede tener algo que ver con los índices existentes en las columnas que usa en el predicado OR.

He probado usando lo siguiente en 11gR2.

create table scott.test as 
select level l, 
     decode(mod(level,2), 1, 1, 2) x, 
     decode(mod(level,2), 1, 2, 1) y, 
     dbms_random.value(1, 3) z from dual 
connect by level < 1000; 
/

begin 
    dbms_stats.gather_table_stats('scott', 'test'); 
end; 
/

entonces me explicó las siguientes consultas en SAPO, (EXPLAIN PLAN FOR)

select x, y, z from scott.test 
    where (floor(z) = 1 and x = 1) or (floor(z) = 2 and y = 1) 
    ; 

SELECT STATEMENT Optimizer Mode=ALL_ROWS  10   4         
    TABLE ACCESS FULL COS_DM.TEST 10  280  4 

select /*+ USE_CONCAT */ x, y, z from scott.test 
where (floor(z) = 1 and x = 1) or (floor(z) = 2 and y = 1) 
; 

SELECT STATEMENT Optimizer Mode=ALL_ROWS  10   4         
    TABLE ACCESS FULL COS_DM.TEST 10  280  4         


select x, y, z from test where (floor(z) = 1 and x = 1) 
union all 
select x, y, z from test where (floor(z) = 2 and y = 1) 
; 

SELECT STATEMENT Optimizer Mode=ALL_ROWS  10   8         
    UNION-ALL            
    TABLE ACCESS FULL COS_DM.TEST 5 140  4         
    TABLE ACCESS FULL COS_DM.TEST 5 140  4         

por lo que parece la pista no está funcionando. Luego añade un índice de los x & Y columnas:

create index test_x on test (x, y); 

begin 
    dbms_stats.gather_table_stats('scott', 'test'); 
end; 
/

Volver a ejecutar las consultas ahora:

select x, y, z from scott.test 
    where (floor(z) = 1 and x = 1) or (floor(z) = 2 and y = 1) 
    ; 

SELECT STATEMENT Optimizer Mode=ALL_ROWS  10   4         
    TABLE ACCESS FULL COS_DM.TEST 10  280  4 

select /*+ USE_CONCAT */ x, y, z from scott.test 
where (floor(z) = 1 and x = 1) or (floor(z) = 2 and y = 1) 
; 

SELECT STATEMENT Optimizer Mode=ALL_ROWS  10   8         
    CONCATENATION            
    TABLE ACCESS FULL COS_DM.TEST 5 140  4         
    TABLE ACCESS FULL COS_DM.TEST 5 140  4         

select x, y, z from test where (floor(z) = 1 and x = 1) 
union all 
select x, y, z from test where (floor(z) = 2 and y = 1) 
; 

SELECT STATEMENT Optimizer Mode=ALL_ROWS  10   8         
    UNION-ALL            
    TABLE ACCESS FULL COS_DM.TEST 5 140  4         
    TABLE ACCESS FULL COS_DM.TEST 5 140  4         

Parece ser que después de añadir el índice (aunque está no se está utilizando) el optimizador decidió usar la pista después de todo!

¿Quizás podría probar esto?

+0

¡Muchas gracias por este análisis! De hecho, no hay índices en (las columnas reales representadas por) 'x' y' y'. Intentaré agregar un índice en 'x'. 'y', sin embargo, no se puede indexar fácilmente en la consulta real, ya que se origina de un' LEFT OUTER JOIN' ... –

+0

El optimizador es quisquilloso sobre estas cosas. Es posible que también intente hacer una conversión de mapa de bits a rowids con un mapa de bits O si indiza cada columna de forma independiente. –

+0

OK, ya veo, intentaré dar seguimiento a esas cosas. Después de algunas investigaciones, resulta que 'CONCATENATION' realmente * se * aplica en la consulta real (cometí un error). Pero aún respondiste mi pregunta simplificada, que parece demasiado simplificada ... :) –

Cuestiones relacionadas