2009-06-14 21 views
5

Tengo una situación de unión de base de datos común que involucra tres tablas. Una tabla, A, es la tabla principal con una clave principal llamada id. Las tablas B y C contienen datos auxiliares para las entradas y A, y cada una también tiene una columna llamada id que es una clave externa que apunta a A. id. Ahora, si quiero todos los datos de A, B y C en una consulta, me gustaría escribir:Agregar condiciones de unión a redudant en Oracle da como resultado un plan diferente

SELECT * 
FROM A 
INNER JOIN B 
ON B.id = A.id 
INNER JOIN C 
ON C.id = A.id 

que por supuesto funciona perfectamente.

Recientemente, nuestro DBA nos dijo que esto es ineficaz en Oracle, y que necesita para unirse a las condiciones entre C y B, así, de la siguiente manera:

SELECT * 
FROM A 
INNER JOIN B 
ON B.id = A.id 
INNER JOIN C 
ON C.id = A.id AND C.id = B.id 

esto parecía redundante para mí, así que naturalmente yo no no creo aquí Hasta que en realidad encontré una consulta lenta que tenía un plan de ejecución terrible, y logré solucionarlo agregando exactamente la condición de combinación faltante. Ejecuté el plan de explicación en ambas versiones: el que no tenía la condición de consulta "redundante" tenía un costo de 1 035, mientras que el "mejorado" tenía 389 (y también había enormes diferencias en la cardinalidad y los bytes). Ambas consultas produjeron exactamente el mismo resultado.

¿Alguien puede explicar por qué esta condición adicional hace la diferencia? Para mí, C y B ni siquiera están relacionados. Tenga en cuenta también que si quita la otra condición de unión, es igual de malo: ambos necesitan estar allí.

Respuesta

2

Lo que tienes son dos problemas.

En primer lugar, con el SQL original, el optimizador calcula el número de filas en A con filas que coinciden con el ID en B que también tienen una fila coincidente en C. El cálculo es incorrecto y se elige el plan incorrecto .

Ahora agrega la condición redundante. Oracle supone que no hay condiciones realmente redundantes (ya que, si lo fueran, un desarrollador inteligente no las incluiría). También supone que cada condición es independiente de las demás. Por ejemplo, un seleccionar donde hair = 'calvo' puede obtener 10% de una tabla, un seleccionar donde gender = 'F' puede obtener 50%. Oracle supondría que un selecto donde hair = 'calvo' y gender = 'F' daría el 5% (mientras que en realidad la calvicie se limita principalmente a los hombres).

Al agregar el predicado 'redundante', Oracle sobreestimará los números o filas que se excluirán y elegirá el plan en consecuencia.

Si, con el predicado redundante, Oracle elige un mejor plan, sugiere que las estimaciones para la consulta original sobreestimaron el número de filas coincidentes. El predicado redundante está contrarrestando eso con una subestimación. Y en este caso, dos errores están haciendo un bien.

No es una solución que recomiendo, pero si funciona .....

PS. Supongo que los tipos de datos de todos los ID son consistentes. Si B.ID y C.ID son fecha y A.ID era carácter, o viceversa, entonces es posible tener algunas filas donde A.ID = B.ID y A.ID = C.ID pero B.ID! = C.ID, porque la conversión implícita puede perder marcas de tiempo.

+1

Estoy de acuerdo contigo Gary: si el plan es mejor con condiciones de combinación redundantes, es porque las estadísticas son inexactas. En general, NO debes proporcionar información redundante. –

+0

Esta es la respuesta más convincente para mí, porque restaura algo de esperanza en Oracle. (Entonces sí, estoy un poco injustamente parcializado.) Ya sea que sea la explicación real, es difícil que alguien responda. – waxwing

1

Esas dos consultas no me parecen del todo iguales.
Por otra parte, no soy el optimizador de Oracle.

Dado que tanto B y C tienen las claves externas de A cuando usted ha hecho esto

INNER JOIN B 
ON B.id = A.id 

el conjunto de resultados resultante (decir que un par de veces rápido) en la que se inscribe en el cuadro C en su segunda consulta es menor, como viste en los resultados de tu plan de ejecución, de lo que es cuando unes la tabla C únicamente a la tabla A como en tu primera consulta.

cosas Así que ha optimizado en la segunda consulta al unirse a la tabla C en el conjunto más pequeño de datos que es la intersección de A y B, y el conjunto más pequeño de datos que es la intersección de A y C.

+1

Heartily disagree. Agregar la segunda condición no hace diferencia en los tamaños reales del conjunto de resultados. Como notaron otros, cambia la estimación del optimizador de los tamaños del conjunto de resultados, por lo tanto, el cambio en el plan de ejecución. Está mal decir que se unió a la tabla C "únicamente a la tabla A" en la primera consulta; se unió a A y B, luego se unió a C en ese conjunto de resultados. La condición de unión pasó a hacer referencia solo a una columna en A, pero lógicamente las columnas de ID en las tres tablas deben ser iguales para las condiciones de unión, por lo que los resultados son los mismos independientemente de cuáles sean los que se utilicen. –

+0

@Dave: Gracias por esa minuciosa explicación. Siento que he aprendido mucho al proporcionar una respuesta pobre a esta pregunta. Ojalá alguien más pueda aprender de esto también. – bernie

2

El optimizador de Oracle no hace suposiciones transitivas sobre la igualdad. Aunque comprendemos que si A = B y A = C, entonces B = C, Oracle no supone que exista una relación entre B & C, a menos que exista uno explícitamente establecido en la cláusula WHERE o en las condiciones de JOIN.

Supongo que tiene otras restricciones en A, B y/o C (en lugar de simplemente seleccionar todo el contenido de las tablas; de lo contrario, su E/S no sería tan baja a menos que sus tablas fueran pequeñas, en cuyo punto la optimización es algo discutible). De modo que realmente hay más restricciones en A, B & C que las especificadas. El optimizador de Oracle examinará todas las tablas en la cláusula FROM, enumerará las restricciones en contra de ellas en la cláusula WHERE, y luego determinará la selectividad de las restricciones en función de los índices para esas tablas. Luego pasará por varias permutaciones de planes de ataque y determinará cuál produce más esperanza (esos son los valores de cardinalidad que se ven en los planes). Sin la condición B = C, excluirá los planes que comienzan con B y pasan a C (o viceversa), y esos pueden ser los mejores planes posibles.

+0

Sí, su suposición es correcta. De hecho, ni siquiera pude encontrar la consulta original cuando escribí esta pregunta, así que escribí una similar que incluye tres tablas con dichas relaciones. Tengo una idea de que la consulta real produjo diferencias aún más dramáticas. Sin embargo, intenté (con la consulta original) incluir solo la condición B = C (pero no la A = C) y obtuve resultados igualmente malos. Lo que era extraño para mí era que tenía que tener tanto A = C como B = C. – waxwing

3

Interesante.

Parece que Oracle solo puede inferir esta igualdad transitiva bajo ciertas circunstancias: Lo llaman Transitive Closure y usted debería poder beneficiarse de él cuando se habilita la reescritura de la consulta.

Pero para estar en el lado seguro, mejor deletrear el predicado redundante usted mismo.

+0

Artículo interesante. ¡Gracias! – waxwing

Cuestiones relacionadas