Lección 8 - Filtrar cálculos de totalización (SQL HAVING)

Curso SQL para principiantes


Si se plantea la siguiente cuestión: ¿Qué ubicaciones del centro de mascotas tienen más de dos ejemplares? Usted podría responder a la tercera pregunta de construcción: ¿Qué requisitos deben cumplir los registros?, lo siguiente: "que la ubicación tenga más de dos ejemplares"; y esa respuesta sería errónea. Esta pregunta nos la formulamos para construir la cláusula WHERE y aplicar filtros a los registros de la tabla, pero como el número de ejemplares de cada ubicación no lo tenemos en ninguna tabla, sino que debemos calcularlo, no podemos aplicar ese filtro en la cláusula WHERE. ¿Dónde entonces?, pues obviamente debemos filtrar las filas de resultados, es decir, de todas las filas resultantes ocultar las que no nos interesen y mostrar el resto. Puede verse como un filtro en segunda instancia, una vez el motor a resuelto la consulta y siempre ajeno a la tabla de datos. Para ello existe una nueva cláusula: HAVING, y en consecuencia una nueva pregunta de construcción: ¿qué requisitos deben cumplir los datos totalizados?

Cláusula HAVING
¿Qué ubicaciones del centro de mascotas tienen más de dos ejemplares?
Construyamos la consulta SQL que resuelve la cuestión planteada con ayuda da las preguntas de construcción:
  • ¿Qué datos nos piden?
    Las ubicaciones.

  • ¿Dónde están los datos?
    En la tabla mascotas.

  • ¿Qué requisitos deben cumplir los registros?
    Ubicaciones que contengan mascotas de alta en el centro.

  • ¿Cómo debemos agrupar los datos?
    Por ubicación.

  • ¿Que requisito han de cumplir los datos totalizados?
    Que el número de ejemplares de las ubicaciones sea mayor a dos.

Consulta SQL:
Código: Seleccionar todo
  select UBICACION , count(*) as EJEMPLARES
    from MASCOTAS
   where ESTADO 
= 'A'
group by UBICACION
  having count
(*) > 2


Resultado:


De las cinco ubicaciones que existen en el centro, solo dos cumplen la condición de la cláusula HAVING. Esta cláusula es de hecho como la cláusula WHERE, pero en lugar de filtrar registros de la tabla, filtra filas de resultado en función de las condiciones que establezcamos sobre las columnas de resultado. En realidad este recurso se usa casi exclusivamente para establecer condiciones sobre las columnas de datos totalizados, puesto que los demás valores, los que están en la tabla, los debemos filtrar en la clausula WHERE.

A fin de ser prácticos consideraremos la clausula HAVING como una cláusula WHERE para los cálculos de totalización. De modo que lo que filtraremos aquí serán cosa del estilo: que la suma sea inferior a..., que la media sea igual a..., que el máximo sea superior a...., o como en el ejemplo: que el recuento de registros sea superior a dos. Siempre sobre cálculos de totalización. Por lo tanto si no hay cláusula GROUP BY, tampoco habrá cláusula HAVING.

Diferencia entre WHERE y HAVING
Veamos con mas detalle la diferencia entre una y otra cláusula. Cuando el motor SQL recorre la tabla para obtener el resultado, ignora los registros que no satisfacen la cláusula WHERE, en el caso anterior ignora los registros que el campo ESTADO no contenga una 'A', y estos registros no son considerados para desarrollar el cálculo. Una vez el motor SQL a recorrido toda la tabla y ha finalizado el cálculo, de las filas resultantes ocultará las que no satisfacen la cláusula HAVING, por lo que en primer lugar: no se ahorra hacer el cálculo para las filas de resultados no mostradas, de lo contrario no podría saber si cumplen o no la condición de la cláusula HAVING, y en segundo lugar, este filtro se aplica en la fase final del proceso que ejecuta el motor SQL, y siempre sobre las filas de resultados escrutando los datos totalizados (COUNT, SUM, MAX, ...), limitándose a mostrar o no una fila de resultado en función de las condiciones establecidas en dicha cláusula.

Todo lo expuesto sobre lógica booleana en la lección 5 es aplicable a la cláusula HAVING, teniendo en cuenta que lo correcto es establecer condiciones sobre las columnas de totalización, y carece de sentido establecer condiciones que podríamos perfectamente establecer en la cláusula WHERE, puesto que en ese caso estaremos haciendo trabajar al motor SQL en vano, es decir, le estaremos obligando a considerar registros que se podría ahorrar ya que finalmente se ocultará la fila o cálculo referente a ese grupo de registros. Por ejemplo:

La siguiente consulta SQL cuenta los ejemplares de alta de la ubicaciones E02 y E03.

Código: Seleccionar todo
  select UBICACION , count(*) as EJEMPLARES
    from MASCOTAS
   where ESTADO 
= 'A' and (UBICACION = 'E02' or UBICACION = 'E03')
group by UBICACION


Pero esta otra consulta SQL cuenta los ejemplares de alta en todas las ubicación y finalmente oculta los que la ubicación no es E02 o E03. Por lo que este el método no es eficiente.
Código: Seleccionar todo
  select UBICACION , count(*) as EJEMPLARES
    from MASCOTAS
   where ESTADO 
= 'A'
group by UBICACION
  having UBICACION 
= 'E02'  or UBICACION = 'E03' 


El resultado de ambas consultas SQL es el mismo, pero hacer lo segundo es no entender el propósito de cada cláusula. Para no caer en este error basta con filtrar siempre las filas de resultado únicamente condicionando columnas de totalización en la cláusula HAVING.

* * *

No queda mucho más que añadir para esta cláusula, veamos otro ejemplo antes de pasar a los ejercicios:

¿Qué ubicaciones del centro de mascotas tienen tan solo un ejemplar?

La consulta que resuelve esta cuestión es casi idéntica a la primera consulta de esta lección. Ahora en lugar de ser el número de ejemplares mayor a dos, tan solo debe haber un ejemplar.

Consulta SQL:
Código: Seleccionar todo
  select UBICACION , count(*) as EJEMPLARES
    from MASCOTAS
   where ESTADO 
= 'A'
group by UBICACION
  having count
(*) = 1


Resultado:


De hecho, como en este caso estamos forzando a que sólo haya un ejemplar, podríamos ocultar la columna de recuento omitiéndola en la cláusula SELECT del siguiente modo:

Consulta SQL:
Código: Seleccionar todo
  select UBICACION as UBICACIONES_CON_UN _EJEMPLAR
    from MASCOTAS
   where ESTADO 
= 'A'
group by UBICACION
  having count
(*) = 1


Resultado:


* * *

Resumen

La cláusula HAVING permite establecer filtros sobre los cálculos de una consulta SQL que realizan las funciones (SUM, COUNT, etc...)

En la cláusula HAVING solo deben condicionarse columnas de cálculo, de modo que si en una consulta SQL no existe la cláusula GROUP BY, tampoco existirá cláusula HAVING.

* * *

Ejercicio
Usando el operador BETWEEN que vimos en las lecciones 3 y 5, construye una consulta que devuelva las ubicaciones del centro de mascotas que tiene entre 2 y 3 ejemplares.




    Lección 7 - Agrupación de datos (SQL GROUP BY)
  • Lección 9 - Ordenación del resultado (SQL ORDER BY)

Creative Commons License Creative Commons License

Este curso está sujeto a la licencia Reconocimiento-NoComercial-SinObraDerivada 3.0 de Creative Commons. Usted puede copiarla, distribuirla y comunicarla públicamente siempre que especifique su autor y deletesql.com; no la utilice para fines comerciales; y no haga con ella obra derivada. Puede usted consultar la licencia completa aquí.




Volver a Curso SQL desde cero

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 10 invitados