sql - from - UNIÓN con la cláusula WHERE
union mysql queries (9)
En mi experiencia, Oracle es muy bueno para impulsar predicados simples . La siguiente prueba se realizó en Oracle 11.2. Estoy bastante seguro de que produce el mismo plan de ejecución en todas las versiones de 10g.
(Por favor, personas, pueden dejar un comentario si ejecuta una versión anterior y prueba lo siguiente)
create table table1(a number, b number);
create table table2(a number, b number);
explain plan for
select *
from (select a,b from table1
union
select a,b from table2
)
where a > 1;
select *
from table(dbms_xplan.display(format=>''basic +predicate''));
PLAN_TABLE_OUTPUT
---------------------------------------
| Id | Operation | Name |
---------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | VIEW | |
| 2 | SORT UNIQUE | |
| 3 | UNION-ALL | |
|* 4 | TABLE ACCESS FULL| TABLE1 |
|* 5 | TABLE ACCESS FULL| TABLE2 |
---------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - filter("A">1)
5 - filter("A">1)
Como puede ver en los pasos (4,5), el predicado se empuja hacia abajo y se aplica antes del género (unión).
No pude hacer que el optimizador empujara hacia abajo una consulta secundaria completa, como
where a = (select max(a) from empty_table)
o unirme. Con las restricciones PK / FK correctas, podría ser posible, pero claramente existen limitaciones :)
Estoy haciendo una UNION
de dos consultas en una base de datos Oracle. Ambos tienen una cláusula WHERE
. ¿Hay alguna diferencia en el rendimiento si hago el WHERE
después de la UNION
de las consultas en comparación con la realización de la UNION
después de la cláusula WHERE
?
Por ejemplo:
SELECT colA, colB FROM tableA WHERE colA > 1
UNION
SELECT colA, colB FROM tableB WHERE colA > 1
comparado con:
SELECT *
FROM (SELECT colA, colB FROM tableA
UNION
SELECT colA, colB FROM tableB)
WHERE colA > 1
Creo que en el segundo caso, realiza una exploración de tabla completa en ambas tablas que afectan el rendimiento. ¿Es eso correcto?
Me aseguraría de tener un índice de ColA, y luego ejecutarlos y cronometrarlos. Eso te daría la mejor respuesta.
Necesita ver los planes de explicación, pero a menos que haya un ÍNDICE o PARTICIÓN en COL_A, está viendo una EXPLORACIÓN DE LA TABLA COMPLETA en ambas tablas.
Con esto en mente, su primer ejemplo es arrojar algunos de los datos como lo hace el ESCANEO DE LA MESA COMPLETA. Ese resultado está siendo ordenado por la UNIÓN, luego se eliminan los datos duplicados. Esto te da tu conjunto de resultados.
En el segundo ejemplo, está extrayendo el contenido completo de ambas tablas. Ese resultado es probable que sea más grande. Entonces la UNIÓN está ordenando más datos, y luego eliminando los duplicados. Luego, el filtro se aplica para darle el conjunto de resultados que busca.
Como regla general, cuanto antes filtre los datos, menor será el conjunto de datos y más rápido obtendrá los resultados. Como siempre, su kilometraje puede variar.
Solo una advertencia
Si lo intentaste
SELECT colA, colB FROM tableA WHERE colA > 1
UNION
SELECT colX, colA FROM tableB WHERE colA > 1
comparado con:
SELECT *
FROM (SELECT colA, colB FROM tableA
UNION
SELECT colX, colA FROM tableB)
WHERE colA > 1
Luego, en la segunda consulta, la colA en la cláusula where tendrá en realidad el colX de la tabla B, lo que la convierte en una consulta muy diferente. Si las columnas se alias de esta manera, puede ser confuso.
creo que dependerá de muchas cosas: ejecute EXPLAIN PLAN
en cada una de ellas para ver lo que selecciona su optimizador. De lo contrario, como sugiere @rayman, ejecútelos y cronomenelos.
NOTA: Si bien mi consejo fue cierto hace muchos años, el optimizador de Oracle ha mejorado, por lo que la ubicación del lugar definitivamente ya no importa aquí. Sin embargo, preferir UNION ALL
vs UNION
siempre será cierto, y SQL portátil debería evitar depender de optimizaciones que pueden no estar en todas las bases de datos.
Respuesta corta, quiere el WHERE
antes de la UNION
y desea usar UNION ALL
si es posible. Si está utilizando UNION ALL
luego verifica la salida EXPLAIN, Oracle podría ser lo suficientemente inteligente como para optimizar la condición WHERE
si se deja después.
La razón es la siguiente. La definición de UNION
dice que si hay duplicados en los dos conjuntos de datos, deben ser eliminados. Por lo tanto, hay un GROUP BY
implícito en esa operación, que tiende a ser lento. Peor aún, el optimizador de Oracle (al menos desde hace 3 años, y no creo que haya cambiado) no intenta impulsar las condiciones a través de un GROUP BY
(implícito o explícito). Por lo tanto, Oracle tiene que construir conjuntos de datos más grandes que los necesarios, agruparlos y solo luego filtrarlos. Por lo tanto, el prefiltrado siempre que sea posible es oficialmente una buena idea. (Esto es, por cierto, por qué es importante poner condiciones en el WHERE
siempre que sea posible en lugar de dejarlas en una cláusula HAVING
).
Además, si sabe que no habrá duplicados entre los dos conjuntos de datos, utilice UNION ALL
. Eso es como UNION
en que concatena conjuntos de datos, pero no trata de deduplicar datos. Esto ahorra una costosa operación de agrupación. En mi experiencia, es bastante común poder aprovechar esta operación.
Como UNION ALL
no tiene un GROUP BY
implícito en él, es posible que el optimizador de Oracle sepa cómo impulsar las condiciones a través de él. No tengo a Oracle sentado para probar, por lo que tendrá que probarlo usted mismo.
SELECT *
FROM (SELECT * FROM can
UNION
SELECT * FROM employee) as e
WHERE e.id = 1;
SELECT * FROM (SELECT colA, colB FROM tableA UNION SELECT colA, colB FROM tableB) as tableC WHERE tableC.colA > 1
Si estamos usando una unión que contiene el mismo nombre de campo en 2 tablas, entonces necesitamos dar un nombre a la consulta secundaria como tabla C (en la consulta anterior). Finalmente, la condición WHERE
debería ser WHERE tableC.colA > 1
SELECT colA, colB FROM tableA WHERE colA > 1
UNION
SELECT colX, colA FROM tableB