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