En el artículo:

Poniendo los JOIN en el orden correcto

ya había mostrado que los INNER JOIN siempre deben escribirse antes que los OUTER JOIN pero no había explicado claramente el motivo del por qué deba ser así.

Me explayaré más sobre ese tema ahora.

Si tenemos una consulta como la siguiente:

Listado 1

SELECT
   MiColumna1,
   MiColumna2,
   MiColumna3
FROM
   MiTabla1
JOIN
   MiTabla2
      ON MiCondición1
JOIN
   MiTabla3
      ON MiCondición2

El optimizador del Firebird puede cambiar el orden de las tablas pues como se trata de un INNER JOIN todas las filas deben estar emparejadas y por lo tanto no importa cual se coloque primero. O sea que si después de la cláusula FROM ponemos a MiTabla1 o la ponemos a MiTabla2 o la ponemos a MiTabla3 el resultado no variará, será idéntico. Así mismo, en el primer o en el segundo JOIN podemos poner a MiTabla1, a MiTabla2, o a MiTabla3, y siempre obtendremos lo mismo.

Sin embargo, aunque el resultado final será el mismo, la eficiencia no será la misma. El optimizador del Firebird realiza su tarea mirando (entre otras cosas) la selectividad de los índices y el tiempo que demorarán. Puede quizás descubrir que para obtener la máxima eficiencia después de la cláusula FROM debe colocar a MiTabla2 y que el primer JOIN debe ser con MiTabla3.

Entonces, si lo considera necesario el optimizador puede tranquilamente poner a otra tabla después del FROM o cambiar el orden de los JOIN, no hay problema con eso porque el resultado obtenido será exactamente el mismo.

En síntesis, el optimizador intentará optimizar la consulta.

¿Y qué ocurre cuándo escribimos un LEFT JOIN?

Listado 2

SELECT
   MiColumna1,
   MiColumna2,
   MiColumna3
FROM
   MiTabla1
LEFT JOIN
   MiTabla2
      ON MiCondición1

Listado 3

SELECT
   MiColumna1,
   MiColumna2,
   MiColumna3
FROM
   MiTabla2
LEFT JOIN
   MiTabla1
      ON MiCondición1

Lo que ocurre es que los resultados devueltos por Listado 2 y por Listado 3 ya no serán idénticos si alguna de las columnas escritas después de la clásula SELECT puede ser NULL o si (este es el caso más común) la cantidad de filas de MiTabla1 no es igual a la cantidad de filas de MiTabla2.

Ejemplo: Si MiTabla1 tiene 5 filas y MiTabla2 tiene 20 filas, el resultado del SELECT puede tener 5 filas (si después de la cláusula FROM escribimos MiTabla1) o puede tener 20 filas (si después de la cláusula FROM escribimos MiTabla2). Evidentemente, son muy diferentes esos resultados.

En el Listado 1 no importaba cual tabla se escribía en el JOIN porque como todas las filas debían estar emparejadas daba lo mismo poner a una tabla o a la otra. Sin embargo, en el Listado 2 y en el Listado 3 eso ya no es verdad, aquí sí importa cual tabla se escribe en el LEFT JOIN y los resultados obtenidos pueden ser muy distintos en cada caso.

Conclusión:

Siempre que ejecutamos un SELECT el optimizador del Firebird intenta que ese SELECT muestre los resultados lo más rápidamente posible, para ello mira entre otras cosas la selectividad de los índices. Cuando usamos INNER JOIN puede cambiar el orden de las tablas porque eso no importa, ya que el resultado final será el mismo y si descubre que cambiando el orden de las tablas los resultados se mostrarán más rápido, entonces cambia el orden de las tablas. Pero cuando usamos LEFT JOIN ya no puede cambiar el orden de las tablas porque en la gran mayoría de los casos los resultados serán distintos. Entonces, deja de optimizar, allí mismo termina su tarea. La primera vez que encuentra un LEFT JOIN, RIGHT JOIN, o FULL JOIN, finaliza la optimización. Y es lógico, porque ya nada puede mejorarse.

Entonces, si nuestro SELECT conteniendo un OUTER JOIN (o sea, LEFT JOIN, RIGHT JOIN, o FULL JOIN) estaba optimizado, todo bien. Si no estaba optimizado, todo mal, ya que el optimizador del Firebird no lo mejorará.

¿Corolario?

Siempre, pero siempre, en todos los casos, debemos escribir primero los INNER JOIN y solamente después de ellos escribir los OUTER JOIN. De esa manera nos aseguraremos de que el optimizador del Firebird haga su trabajo y mejore si es posible la velocidad con la cual se obtienen los resultados.

Artículos relacionados:

Poniendo los JOIN en el orden correcto

El índice del blog Firebird21

El foro del blog Firebird21