¿Usar subconsultas o usar joins?

5 comentarios

En muchas consultas se puede obtener exactamente el mismo resultado si usamos una subconsulta o si usamos un JOIN, entonces ¿cuál es preferible usar?

Veamos un ejemplo.

Listado 1.


SELECT
   VTC_IDENTI,
   VTC_FECHAX,
   VTC_IDECLI,
   CLI_NOMBRE
FROM
   VENTASCAB
JOIN
   CLIENTES
      ON VTC_CODSUC = CLI_CODSUC AND
         VTC_IDECLI = CLI_IDENTI
WHERE
   VTC_CODSUC = 0 AND
   VTC_IDENTI BETWEEN 31 AND 39
ORDER BY
   VTC_IDENTI

Listado 2.

SELECT
   VTC_IDENTI,
   VTC_FECHAX,
   VTC_IDECLI,
   (SELECT CLI_NOMBRE FROM CLIENTES WHERE VTC_CODSUC = CLI_CODSUC AND    VTC_IDECLI = CLI_IDENTI)
FROM
   VENTASCAB
WHERE
   VTC_CODSUC = 0 AND
   VTC_IDENTI BETWEEN 31 AND 39
ORDER BY
   VTC_IDENTI

JOIN1.png

Captura 1. Si haces clic en la imagen la verás más grande

Tanto si ejecutamos el Listado 1. como si ejecutamos el Listado 2. el resultado obtenido será exactamente el mismo y el rendimiento también será exactamente el mismo, ninguno será preferible al otro.

Por lo tanto, desde ese punto de vista, ejecutar a uno o al otro es indiferente, cuestión de gustos nomás.

Sin embargo, hay que tener en cuenta que las subconsultas le cuestan más al Firebird optimizar. En consultas sencillas como las mostradas arriba no habrá diferencias pero en subconsultas complicadas ya será otro tema.

Por lo tanto, lo recomendable es usar JOIN y no subconsultas. Si usamos subconsultas podríamos obtener el mismo rendimiento que si usamos JOIN pero jamás obtendríamos un mejor rendimiento. En cambio si usamos JOIN a veces podríamos obtener un mejor rendimiento que si usamos subconsultas.

Artículos relacionados:

El índice del blog Firebird21

El foro del blog Firebird21

 

 

La cláusula HAVING: filtrando las filas agrupadas

2 comentarios

En artículos anteriores ya hemos visto a la cláusula GROUP BY, la forma de usarla, y su utilidad práctica. Así que ahora nos preguntamos ¿y si solamente nos interesan algunas de esas filas agrupadas?

Pues en nuestro socorro viene la cláusula HAVING.

Después de haber agrupado a las filas usando la cláusula GROUP BY, podemos establecer las condiciones que esas filas agrupadas deben cumplir.

Esto es algo opcional, no siempre se requiere filtrar a las filas agrupadas.

Veamos un ejemplo:

GROUP1

Captura 1. Si haces clic en la imagen la verás más grande

En la tabla MOVIMCAB (cabecera de movimientos) tenemos los datos de cada venta realizada. Queremos agrupar a esas filas por identificador del cliente, para ver el total vendido a cada cliente.

Listado 1.

SELECT
   MVC_IDECLI,
   SUM(MVC_TOTALX) AS TOTAL_VENTA
FROM
   MOVIMCAB
GROUP BY
   MVC_IDECLI

GROUP3

Captura 2. Si haces clic en la imagen la verás más grande

En el Listado 1. tenemos la solución, después de ejecutarlo veremos la Captura 2. Todo bien hasta ahí. Pero supongamos que ahora solamente nos interesa ver los clientes a quienes les hemos vendido por 1.000.000 ó más.

Listado 2.

SELECT
   MVC_IDECLI,
   SUM(MVC_TOTALX) AS TOTAL_VENTA
FROM
   MOVIMCAB
GROUP BY
   MVC_IDECLI
HAVING
   SUM(MVC_TOTALX) >= 1000000

GROUP7

Captura 3. Si haces clic en la imagen la verás más grande

En el Listado 2. hemos usado a la cláusula HAVING y el resultado obtenido lo vemos en la Captura 3.

¿Qué hace la cláusula HAVING?

Filtra (o sea, le pone condiciones) a las filas que estarán en el conjunto resultado de nuestra consulta. La cláusula HAVING solamente puede usarse después que la cláusula GROUP BY y solamente allí, en ningún otro lugar es admitida.

En la Captura 2. hemos visto el resultado de agrupar a las ventas por clientes, en la Captura 3. vemos el resultado de poner una condición a las filas de la Captura 2.

Es decir que tenemos a las filas que vemos en la Captura 2. pero solamente algunas de esas filas nos interesan. Allí es donde usamos a la cláusula HAVING, para que solamente las filas de la Captura 2. que cumplen nuestra condición sean mostradas.

La cláusula HAVING es muy similar a la cláusula WHERE, pero mientras que WHERE filtra a todas las filas, HAVING solamente filtra a las filas que obtuvimos después de un agrupamiento.

A veces, podemos usar WHERE o podemos usar HAVING, y el resultado obtenido es el mismo. A veces solamente podemos usar a HAVING, no está permitido usar WHERE. Veamos ejemplos:

Con WHERE y con HAVING se obtiene el mismo resultado

Listado 3.

SELECT
   MVC_IDECLI,
   SUM(MVC_TOTALX) AS TOTAL_VENTA
FROM
   MOVIMCAB
WHERE
   MVC_IDECLI >= 5
GROUP BY
   MVC_IDECLI

Listado 4.

SELECT
   MVC_IDECLI,
   SUM(MVC_TOTALX) AS TOTAL_VENTA
FROM
   MOVIMCAB
GROUP BY
   MVC_IDECLI
HAVING
   MVC_IDECLI >= 5

GROUP8

Captura 4. Si haces clic en la imagen la verás más grande

Tanto si ejecutamos el Listado 3. o el Listado 4. el resultado que obtendremos será el mismo: el mostrado en la Captura 4.

¿Por qué?

Porque la condición de filtro fue puesta en la columna MVC_IDECLI, o sea en una columna que no es una función agregada: COUNT(), SUM(), MAX(), MIN(), AVG().

En cambio, si la condición la ponemos en una columna que proviene de una función agregada, solamente podremos usar la cláusula HAVING, no podemos usar una función agregada en la cláusula WHERE.

Listado 5.

SELECT
   MVC_IDECLI,
   SUM(MVC_TOTALX) AS TOTAL_VENTA
FROM
   MOVIMCAB
WHERE
   SUM(MVC_TOTALX) >= 1000000
GROUP BY
   MVC_IDECLI

El Listado 5. es incorrecto, el Firebird no lo aceptará y nos mostrará el mensaje: «Cannot use an aggregate function in a WHERE clause, use HAVING instead.«

Por lo tanto, si queremos filtrar a las funciones agregadas no podremos escribirlas en la cláusula WHERE, solamente podremos escribirlas en la cláusula HAVING.

En síntesis, si tu condición de filtro no involucra a una función agregada puedes usar la cláusula WHERE o la cláusula HAVING para filtrar a esas filas, como prefieras, es tu elección, al Firebird le da lo mismo. Pero si tu condición de filtro involucra a una función agregada solamente puedes usar a la cláusula HAVING, no tienes otra posibilidad.

No se pueden usar alias en la cláusula HAVING

Listado 6.

SELECT
   MVC_IDECLI,
   SUM(MVC_TOTALX) AS TOTAL_VENTA
FROM
   MOVIMCAB
GROUP BY
   MVC_IDECLI
HAVING
   TOTAL_VENTA >= 1000000

En el Listado 6. hay un error y el Firebird nos avisará con el mensaje: «Column unknown. TOTAL_VENTA.«

¿Cuál es el error? Que quisimos usar al alias de una columna (en este caso el alias es TOTAL_VENTA) en la cláusula HAVING y eso no está permitido. La forma correcta es la mostrada en el Listado 2., que sí funcionará perfectamente.

Conclusión:

Si después de agrupar a las filas nos interesa ver solamente a algunas de ellas, entonces podemos establecer la condición (o condiciones) que deben cumplir esas filas en la cláusula HAVING.

La cláusula HAVING solamente puede escribirse inmediatamente después que la cláusula GROUP BY, en ningún otro lugar es admitida.

A veces podemos filtrar a las filas usando la cláusula WHERE o usando la cláusula HAVING, indistintamente. Eso ocurre cuando nuestro filtro no involucra a una función agregada. En tales casos ya es preferencia de cada uno usar WHERE o usar HAVING.

Sin embargo, si la condición de filtro involucra a una (o más de una) función/es agregada/s entonces solamente podemos usar la cláusula HAVING, usar la cláusula WHERE no está permitido en esos casos.

Otro punto importante a recordar es que los alias de las columnas no pueden usarse con la cláusula HAVING.

Artículos relacionados:

Entendiendo la cláusula GROUP BY: agrupando datos

La cláusula GROUP BY requiere estar ordenada

El índice del blog Firebird21

El foro del blog Firebird21

La cláusula GROUP BY requiere estar ordenada

2 comentarios

Cuando se escribe la cláusula GROUP BY debemos tener en cuenta que el Firebird requiere que las columnas involucradas se encuentren ordenadas. Si no hay un índice que pueda usar entonces él se encargará de realizar el ordenamiento pero esa tarea puede demorar mucho tiempo en tablas grandes.

Listado 1.

SELECT
   VTC_IDECLI,
   VTC_FECHAX
FROM
   VENTASCAB
GROUP BY
   VTC_IDECLI,
   VTC_FECHAX

Aquí, como no hay un índice que el Firebird pueda usar entonces se ve obligado a crear un índice temporario, como podemos ver en el PLAN respectivo.

PLAN SORT ((VENTASCAB NATURAL))

La palabra SORT nos indica que el Firebird tuvo que ordenar a la tabla VENTASCAB. Ese ordenamiento puede demorar mucho si las filas son muy numerosas.

Listado 2.

SELECT
   VTC_CODSUC,
   VTC_IDECLI,
   VTC_FECHAX
FROM
   VENTASCAB
GROUP BY
   VTC_CODSUC,
   VTC_IDECLI,
   VTC_FECHAX

En este caso el PLAN que utilizó fue:

PLAN (VENTASCAB ORDER IDX_VENTASCAB3)

El cual nos indica que ordenó al resultado usando el índice de nombre IDX_VENTASCAB3

CUIDADO: Si en la cláusula WHERE tenemos un índice que se pueda usar entonces el Firebird lo utilizará aunque el orden de las columnas del SELECT no coincida con el orden de las columnas en el WHERE. Pero eso mismo no ocurre con la cláusula GROUP BY, porque aquí el orden sí importa. Ya hemos visto en un artículo anterior que los resultados obtenidos dependen del orden en que escribamos las columnas en la cláusula GROUP BY. Por lo tanto, para que un índice pueda ser utilizado deberán coincidir exactamente las columnas de ese índice con las columnas escritas en la cláusula GROUP BY. Ejemplificando: si en el GROUP BY del Listado 2. se hubiera escrito VTC_IDECLI, VTC_CODSUC, VTC_FECHAX, entonces el índice IDX_VENTASCAB3 no hubiera sido usado. Para usar a ese índice las columnas del GROUP BY deben escribirse sí o sí como VTC_CODSUC, VTC_IDECLI, VTC_FECHAX, porque ese es el orden de esas columnas en el índice. O sea, como el índice fue creado así:

Listado 3.

CREATE INDEX IDX_VENTASCAB3 ON VENTASCAB(VTC_CODSUC,VTC_IDECLI,VTC_FECHAX);

Solamente será usado si en la cláusula GROUP BY escribimos alguna de estas 3 alternativas:

VTC_CODSUC

VTC_CODSUC, VTC_IDECLI

VTC_CODSUC, VTC_IDECLI, VTC_FECHAX

Si en la cláusula GROUP BY escribimos cualquier otra combinación de columnas, el índice IDX_VENTASCAB3 no será usado.

Conclusión:

Cuando usamos la cláusula GROUP BY el Firebird sí o sí necesita que las columnas involucradas estén ordenadas. Si no existe un índice que pueda utilizar entonces él se encarga de ordenar a las columnas pero realizar esa tarea puede ser muy lento en tablas que tienen muchas filas. Por lo tanto en general nuestra mejor política es proveerle de ese índice.

Lo cual significa que cada vez que uses la cláusula GROUP BY debes preguntarte: ¿existe un índice para esas columnas?

Si no existe, debes pensar si es conveniente crearlo. Si aunque no existe un índice los resultados se obtienen muy rápido (porque son muy pocas las filas agrupadas) entonces no vale la pena crear ese índice.

Artículos relacionados:

Usando un PLAN

Algo más sobre PLAN

Entendiendo el contenido de un PLAN

Poniendo los JOIN en el orden correcto

Entendiendo la cláusula GROUP BY: agrupando datos

El índice del blog Firebird21

El foro del blog Firebird21

Entendiendo la cláusula GROUP BY: agrupando datos

4 comentarios

El lenguaje SQL nos permite agrupar filas. ¿Qué significa agrupar filas? Que todas aquellas filas que tienen en una (o más de una) columna/s valores en común aparecerán una sola vez en nuestra consulta.

Ejemplos:

  • Para cada fecha, queremos ver el total vendido en esa fecha.
  • Para cada cliente, queremos ver el total vendido a ese cliente.
  • Para cada producto, queremos ver la cantidad total vendida de ese producto.
  • Para cada vendedor, queremos ver el promedio mensual de ventas de ese vendedor

Hay dos formas de resolver algo así:

  1. Sin agrupar las filas
  2. Agrupando las filas

Ventas a un cliente sin agrupar las filas

Tenemos una tabla llamada MOVIMCAB (cabecera de movimientos) donde guardamos los datos de las ventas que realizamos, y si consultamos las ventas realizadas entre el 1 de agosto y el 3 de agosto del año 2015, veremos algo como:

GROUP1

Captura 1. Si haces clic en la imagen la verás más grande

Donde MVC_FECHAX es la fecha de la venta, MVC_IDECLI es el identificador del cliente, MVC_TIPDOC es el tipo de Factura (1=contado, 2=crédito), MVC_NRODOC es el número de la Factura y MVC_TOTALX es el total de esa venta.

Y ahora queremos saber: ¿cuánto le hemos vendido en total al cliente cuyo identificador es 5?. Podemos fácilmente responder a esa pregunta así:

Listado 1.

SELECT
   SUM(MVC_TOTALX) AS TOTAL_VENTA
FROM
   MOVIMCAB
WHERE
   MVC_IDECLI = 5

GROUP2

Captura 2. Si haces clic en la imagen la verás más grande

Y como vemos en la Captura 2., funcionó perfectamente. Respondimos muy bien a la pregunta.

Sin embargo, ¿qué pasará si necesitamos conocer el total vendido no a un solo cliente sino a muchos clientes? Que tendremos que repetir el Listado 1. muchas veces, una vez por cada cliente que nos interese. Si tenemos 2000 clientes tendremos que repetir esa consulta 2000 veces, algo totalmente impráctico y mortalmente aburrido.

Hay una solución muchísimo más eficiente: usar la cláusula GROUP BY.

Ventas a los clientes, agrupando las filas

Listado 2.

SELECT
   MVC_IDECLI,
   SUM(MVC_TOTALX) AS TOTAL_VENTA
FROM
   MOVIMCAB
GROUP BY
   MVC_IDECLI

GROUP3

Captura 3. Si haces clic en la imagen la verás más grande

Aquí, para cada cliente, hemos obtenido el monto total vendido, como puedes comprobar mirando la Captura 1. y comparándola con la Captura 3.

Evidentemente, escribir el Listado 2. una sola vez es tremendamente mejor que escribir el Listado 1. unas 2000 veces.

¿Cómo funciona la cláusula GROUP BY?

  1. Normalmente se la utiliza con las funciones agregadas: COUNT(), SUM(), MAX(), MIN(), AVG()
  2. Agrupa todas las filas que en una columna (o más de una) columna/s tienen valores en común
  3. Todas las columnas que no sean funciones agregadas, deben especificarse en la cláusula GROUP BY

Total de ventas, por fechas

En el Listado 2. usamos GROUP BY para obtener el total vendido a cada cliente. Ahora usaremos esa misma cláusula pero para obtener el total vendido en cada fecha.

Listado 3.

SELECT
   MVC_FECHAX,
   SUM(MVC_TOTALX) AS TOTAL_VENTA
FROM
   MOVIMCAB
GROUP BY
   MVC_FECHAX

GROUP4

Captura 4. Si haces clic en la imagen la verás más grande

Y aquí hemos obtenido el total vendido en cada fecha. La diferencia entre el Listado 2. y el Listado 3. es la columna que hemos elegido para agrupar. Al agrupar por identificador del cliente hemos obtenido el total vendido a cada cliente; al agrupar por fecha hemos obtenido el total vendido en cada fecha.

Podemos agrupar por cualquier columna que nos interese. Desde luego que para que tenga sentido en 2 ó más filas esa columna debe tener al menos un valor en común. Por ejemplo, sería una tontería agrupar por la columna MVC_NRODOC ¿por qué? porque no hay valores repetidos allí. Agrupando o no agrupando obtendremos los mismos resultados así que hemos escrito más (porque escribimos la cláusula GROUP BY) y obtuvimos lo mismo que si no la hubiéramos escrito. Pérdida de tiempo.

Por lo tanto, debemos agrupar por cualquier columna que tenga o pueda tener valores repetidos.

Agrupar por varias columnas

En los ejemplos anteriores siempre hemos agrupado usando una sola columna, pero eso no tiene siempre que ser así, podemos agrupar usando varias columnas también.

Por ejemplo, para cada tipo de Factura queremos obtener el total vendido en cada fecha.

Listado 4.

SELECT
   MVC_TIPDOC,
   MVC_FECHAX,
   SUM(MVC_TOTALX) AS TOTAL_VENTA
FROM
   MOVIMCAB
GROUP BY
   MVC_TIPDOC,
   MVC_FECHAX

GROUP5

Captura 5. Si haces clic en la imagen la verás más grande

Aquí hemos agrupado primero por MVC_TIPDOC (1=Factura Contado, 2=Factura Crédito) y luego por fecha de la venta.

Así sabemos que el 1 de agosto hubo ventas con Facturas Contado por un total de 835.000 y ese mismo día las ventas con Facturas Crédito ascendieron a 740.000

El Listado 4. nos permitirá responder a: «Muéstrame el total de Facturas Contado vendidas en cada fecha y luego el total de Facturas Crédito vendidas en cada fecha».

Podríamos cambiar el orden, para que primero aparezcan las fechas

Listado 5.

SELECT
   MVC_FECHAX,
   MVC_TIPDOC,
   SUM(MVC_TOTALX) AS TOTAL_VENTA
FROM
   MOVIMCAB
GROUP BY
   MVC_FECHAX,
   MVC_TIPDOC

GROUP6

Captura 6. Si haces clic en la imagen la verás más grande

Aunque el orden de las columnas y el orden de las filas está cambiado, vemos que el TOTAL_VENTA sigue siendo correcto. La consulta del Listado 5. nos permitiría responder lo siguiente: «Para cada fecha muéstrame el total vendido con Factura Contado y el total vendido con Factura Crédito».

Un error común de principiantes

Un error muy común entre los principiantes es querer que la consulta muestre también una columna que no fue agrupada. Eso no está permitido, no es posible.

Listado 6.

SELECT
   MVC_FECHAX,
   MVC_NRODOC,
   SUM(MVC_TOTALX) AS TOTAL_VENTA
FROM
   MOVIMCAB
GROUP BY
   MVC_FECHAX

Si intentamos ejecutar el Listado 6. veremos el mensaje: «Invalid expression in the select list (not contained in either an aggregate function or the GROUP BY clause).«

¿Cuál es el error? Que la columna MVC_NRODOC no se encuentra en la cláusula GROUP BY. Ese SELECT es por lo tanto incorrecto.

Cuando usamos la cláusula GROUP BY al Firebird le estamos pidiendo que agrupe a todas las filas que en una columna (o más de una columna) tenga/n valores repetidos. Para eso sirve la cláusula GROUP BY.

Si en la lista de columnas del SELECT escribimos una columna y a esa columna no la escribimos también en el GROUP BY entonces evidentemente no estaremos agrupando según esa columna. Y no tiene sentido agrupar por algunas columnas sí y por algunas columnas no. O se agrupa por todas las columnas o por ninguna. Agrupar solamente algunas columnas es imposible de realizar.

Si algo así estuviera permitido entonces el Listado 6. nos mostraría una fecha, el número de una Factura (¿de cuál Factura? podemos tener muchas en una fecha), y el total vendido en esa fecha. No nos mostraría la venta de esa Factura sino la venta acumulada de toda la fecha. Eso es un sinsentido. Ninguna pregunta práctica podríamos responder con el Listado 6.

Por ese motivo el Firebird rechazará una consulta como la del Listado 6.

¿Y por qué no agrega a la cláusula GROUP BY todas las columnas que se encuentran en el SELECT?

Podríamos pensar que algo así sería muy práctico: si escribimos una columna en el SELECT y no la escribimos en el GROUP BY, que el Firebird se encargue de agregarla allí.

Bien, eso tiene sus problemas: 1) que el orden de las columnas del SELECT puede ser distinto al orden de las columnas del GROUP BY, no hay obligación de mantener el orden y ya vimos que cambiando el orden de las columnas del GROUP BY se obtienen resultados distintos, por lo tanto el Firebird podría colocar a la columna faltante en el lugar equivocado y no obtendríamos los resultados deseados. 2) Que el Firebird no puede adivinar lo que tú quieres hacer y por lo tanto no puede saber si esa columna sobrante la escribiste por equivocación o si tu equivocación fue no incluirla en el GROUP BY.

En resumen: como con la cláusula GROUP BY se agrupan filas que en una columna (o más de una columna) tiene/n valores repetidos todas las columnas que se escriban en el SELECT deben también, sí o sí, escribirse en el GROUP BY. Eso es lo lógico y lo único que tiene sentido.

Conclusión:

La cláusula GROUP BY es muy útil para agrupar filas que en una columna (o en más de una columna) tienen valores repetidos. Podríamos dejar de usarla y obtendríamos los mismos resultados pero si no la usamos generalmente escribiremos muchísimo pero muchísimo más.

Todas las columnas que escribimos en el SELECT debemos también escribir en el GROUP BY porque de lo contrario obtendremos un error. No tiene sentido tener una columna en el SELECT y no tenerla también en el GROUP BY.

Artículos relacionados:

El índice del blog Firebird21

El foro del blog Firebird21

Entendiendo la recursividad en los SELECT

Deja un comentario

En un artículo anterior ya vimos un ejemplo de un SELECT recursivo, el cual fue muy útil, así que ahora explicaremos mejor como crear SELECTs recursivos.

  1. Se construye usando CTE (Common Table Expression)
  2. Un SELECT recursivo es una UNION entre miembros recursivos y no recursivos
  3. Debe tener al menos un miembro no recursivo al cual se le llama anchor (ancla)
  4. El miembro (o miembros) no recursivo/s debe colocarse antes que el miembro (o miembros) recursivo/s
  5. El enlace entre el miembro (o miembros) no recursivo/s y el miembro (o miembros) recursivo/s se hace mediante UNION ALL
  6. Las uniones entre los miembros no recursivos puede ser de cualquier tipo
  7. Después de la palabra WITH hay que escribir la palabra RECURSIVE
  8. El nombre de la tabla virtual puede usarse solamente en un FROM o en un INNER JOIN
  9. En el miembro (o miembros) recursivo/s no puede usarse un OUTER JOIN (LEFT JOIN, RIGHT JOIN, FULL JOIN). En el miembro (o miembros) no recursivo/s sí pueden usarse los OUTER JOIN
  10. Un SELECT recursivo usa mucha menos memoria y ciclos de CPU que un stored procedure recursivo equivalente
  11. En el miembro (o miembros) recursivo/s no se pueden usar agregados (DISTINCT, GROUP BY, HAVING). En el miembro (o miembros) no recursivos, sí
  12. En el miembro (o miembros) recursivo/s no se pueden usar funciones agregadas (COUNT(), SUM(), AVG(), MAX(), MIN(), LIST()). En el miembro (o miembros) no recursivo/s, sí
  13. La máxima profundidad admitida es 1024

Listado 1.

WITH RECURSIVE MiTablaVirtual AS (
   SELECT <datos_padre>
   UNION ALL
   SELECT <datos_hijo> JOIN <MiTablaVirtual> ON <enlace_con_el_padre>
)

SELECT * FROM MiTablaVirtual
  1. La ejecución se inicia en el primer SELECT
  2. El conjunto resultado obtenido del primer SELECT se llama «MiTablaVirtual» (en este ejemplo) y como cuando finaliza ya existe «MiTablaVirtual» entonces a ésta se la puede referenciar en el segundo SELECT.
  3. A «MiTablaVirtual» se le unen los conjuntos resultados obtenidos del segundo SELECT
  4. Cuando de la ejecución del segundo SELECT se obtiene un conjunto resultado vacío (es decir, ninguna fila) se regresa un nivel y se obtiene la siguiente fila, si es posible
  5. Cuando ya no se pueden obtener filas, termina la recursividad
  6. A continuación del CTE hay que usar la tabla virtual creada porque de lo contrario todo lo que se hizo en ella se perderá
  7. Los nombres de las columnas que pueden usarse externamente de la tabla virtual son los nombres de columnas que especificamos en el segundo SELECT. O sea que en el «SELECT * FROM MiTablaVirtual» del Listado 1. veremos los nombres de las columnas del segundo SELECT.

Tipos de recursión

La recursión generalmente es útil cuando podemos representar a los datos en forma de árbol, como en el Gráfico 1., que es el caso más común. También es muy útil si los datos se pueden representar como pilas, colas o listas; cuando no es así en general no se puede o no se debe usar recursión.

RECURSIÓN01

Gráfico 1. Si haces clic en la imágen la verás más grande

Hay dos tipos de recursión:

  • De arriba hacia abajo
  • De abajo hacia arriba

Cuando la recursión es de abajo hacia arriba, puede reemplazarse con iteración, es decir con un ciclo WHILE que vaya subiendo al nodo superior hasta descubrir que ya no hay más nodos.

El Firebird tiene actualmente un límite de 1024 llamadas recursivas. O sea que en el Gráfico 1. podríamos tener como máximo 1024 nodos. Esto es mucho más que suficiente para la gran mayoría de las aplicaciones … pero no para todas.

Artículos relacionados:

Usando recursividad con CTE

Stored procedures recursivos

El índice del blog Firebird21

El foro del blog Firebird21