Volver a la lista de artículos Artículos
Lectura de 12 minutos

¿Cuál es la diferencia entre las cláusulas WHERE y HAVING en SQL?

A medida que la cantidad de datos en el mundo crece día a día, aumenta la necesidad de gestionar y consultar los datos de manera eficiente. SQL ofrece formas de manipular y consultar datos a cualquier escala. Como parte importante de esto, las cláusulas WHERE y HAVING permiten filtrar los datos según las necesidades, pero no son lo mismo. Explicaremos en detalle las diferencias entre ellas.

Una de las tareas más comunes en la manipulación y consulta de datos es el filtrado mediante las cláusulas WHERE y HAVING. Aunque ambas filtran datos basándose en condiciones y/o funciones definidas por el usuario, existen importantes diferencias en la forma en que se ejecuta cada cláusula.

Este artículo explica en detalle cuándo usar WHERE o HAVING. Explicaremos brevemente el flujo de proceso lógico de una consulta SQL que incluya las cláusulas WHERE y/o HAVING, seguido de algunos escenarios de práctica y ejemplos para que los resolvamos juntos.

Flujo de Proceso Lógico para Consultas con WHERE y HAVING

El orden en que se escribe una consulta SQL difiere del orden de ejecución. Veamos lo siguiente como ejemplo:

SELECT country_id, city, MIN(salary), MAX(salary) 
FROM hr.emp_details_view
WHERE country_id IN ('US', 'UK')
GROUP BY country_id, city
HAVING MIN(salary) < 15000
ORDER BY country_id; 

El flujo de proceso lógico de una consulta SQL está diagramado a continuación.

Orden de Ejecución en Consultas SQL

Figura 1: Orden de ejecución de las consultas SQL.


  1. El proceso lógico de una consulta SQL comienza con una sentencia FROM, que recoge los datos de las tablas enumeradas en la consulta. También puede contener una cláusula JOIN que combina dos o más tablas utilizando el operador ON.

    Tenga en cuenta que las condiciones utilizadas en la cláusula WHERE también pueden utilizarse en el operador ON de la cláusula JOIN. Asimismo, las tablas combinadas mediante condiciones en el operador ON de la cláusula JOIN pueden utilizarse en la cláusula WHERE. Sin embargo, se debe utilizar WHERE sólo para las condiciones que filtran filas individuales. Esto mejora la legibilidad de la consulta y permite que el operador ON maneje sólo la combinación de las tablas, que es su propósito.

  2. El siguiente paso en el flujo del proceso lógico es la cláusula WHERE, que filtra los registros a los que cumplen la(s) condición(es) y/o la(s) función(es) definida(s) por el usuario y los pasa.
  3. A la cláusula WHERE le sigue la cláusula GROUP BY, que agrupa los registros recibidos de la condición WHERE. Por ejemplo, la(s) columna(s) listada(s) en la cláusula GROUP BY puede(n) ser los equipos, y otras columnas son agregadas por la(s) función(es) de agregación y asignadas a los grupos/equipos correspondientes.
  4. A continuación está la cláusula HAVING, que filtra los grupos creados en GROUP BY en lugar de los registros individuales.
  5. En este punto, el procesamiento lógico pasa al comando SELECT. Evalúa qué columnas van a ser enviadas a la salida. También evalúa cualquier palabra clave como UNIQUE, DISTINCT, y TOP, si se incluye.
  6. La cláusula ORDER BY se ejecuta al final del flujo del proceso lógico. Ordena los datos por la(s) columna(s) especificada(s) en ella y en orden ascendente por defecto.

Primero, lo esencial: Especificación de condiciones en WHERE y HAVING

Existe una amplia gama de operadores que nos ayudan a crear y combinar condiciones en las cláusulas WHERE y HAVING:

  • signos de comparación: <, >, <=, >=, =, <>
  • comprobación de valores nulos: IS NULL, IS NOT NULL
  • comodines: LIKE, '%=_'
  • comprobación de rangos: BETWEEN
  • comprobación de la presencia en un conjunto: IN
  • prueba de existencia: EXISTS
  • signos de comparación cuantificados: ALL, ANY, SOME
  • combinación lógica de las condiciones: AND, OR, NOT

Los operandos de una cláusula WHERE pueden incluir las columnas enumeradas en SELECT, excepto las columnas utilizadas en las funciones de agregación. En cambio, HAVING filtra por valores resultantes de la función de agregación como SUM(), COUNT(), y AVG(), entre otros.

Ejemplos

Las tablas employee y emp_details_view, que se muestran parcialmente a continuación, se utilizan a lo largo de los próximos ejemplos.

SELECT * 
FROM hr.employees;
EMPLOYEE_IDLAST_NAMEMANAGER_IDHIRE_DATESALARYJOB_ID
101Kochhar10021-SEP-0517000AD_VP
102De Haan10013-JAN-0117000AD_VP
108Greenberg10117-AUG-0212008FI_MGR
103Hunold10203-JAN-069000IT_PROG
105Austin10325-JUN-054800IT_PROG
116Baida11424-DEC-052900PU_CLERK
117Tobias11424-JUL-052800PU_CLERK
145Russell10001-OCT-0414000SA_MAN
148Cambrault10015-OCT-0711000SA_MAN
SELECT * 
FROM hr.emp_details_view;
EMPLOYEE_IDLAST_NAMEFIRST_NAMESALARYDEPARTMENT_IDCOUNTRY_IDCITYLOCATION_ID
103HunoldAlexander900060USSouthlake1400
105AustinDavid480060USSouthlake1400
101KochharNeena1700090USSeattle1700
102De HaanLex1700090USSeattle1700
108GreenbergNancy12008100USSeattle1700
116BaidaShelli290030USSeattle1700
117TobiasSigal280030USSeattle1700
145RussellJohn1400080UKOxford2500
148CambraultGerald1100080UKOxford2500

Escenario I: La cláusula WHERE

La cláusula WHERE se utiliza para filtrar filas individuales de datos obtenidos de la base de datos mediante SELECT. Los registros que no cumplen la(s) condición(es) de WHERE no se incluyen en la salida.

Veamos un ejemplo:

where age > 25 AND city='New York'

Se pasan sólo los registros cuya edad es superior a 25 años y cuya city es Nueva York.

En la sintaxis de la consulta, la cláusula WHERE se utiliza directamente después de SELECT y FROM. Filtra las filas individuales -los registros sin agrupar- obtenidas por la consulta, para determinar qué registros deben pasarse a la cláusula GROUP BY.

Examinemos el resultado de la siguiente consulta que utiliza la cláusula WHERE.

SELECT last_name, hire_date, manager_id 
FROM hr.employees
WHERE last_name LIKE '%a%' 
AND (hire_date BETWEEN '01-JAN-05' AND '01-JAN-07') 
AND manager_id IS NOT NULL;
LAST_NAMEHIRE_DATEMANAGER_ID
Baida24-DEC-05114
Kochhar21-SEP-05100
Tobias24-JUL-05114

En el ejemplo anterior se obtienen las columnas last_name, hire_date, y manager_id de la tabla hr.employee. La cláusula WHERE filtra la salida como sigue:

  • last_name debe incluir una letra "a";
  • hire_date debe ser mayor o igual que el 01-ENE-05 y menor o igual que el 01-ENE-07; y
  • manager_id debe tener un valor y no ser nulo.

Sólo se presentan en la salida los registros que cumplen las condiciones de WHERE.

Veamos otro ejemplo del mismo escenario:

SELECT employee_id, salary, job_id 
FROM hr.employees
WHERE (salary < 3000 OR salary = 9000) 
AND job_id IN ('IT_PROG', 'FI_MGR', 'PU_CLERK'); 
EMPLOYEE_IDSALARYJOB_ID
1039000IT_PROG
1162900PU_CLERK
1172800PU_CLERK

Este ejemplo da salida a las columnas employee_id, salary, y job_id de la tabla hr.employee. La cláusula WHERE filtra la salida como sigue:

  • salary debe ser inferior a 3000, o debe ser igual a 9000; y
  • job_id debe ser igual a cualquier valor indicado en el operador IN.

Al igual que en la consulta anterior, la salida sólo contiene los registros que cumplen la condición WHERE.

Tenga en cuenta que WHERE no puede contener condiciones que incluyan funciones de agregación. Ese es el trabajo para HAVING.

Escenario II: La cláusula HAVING

HAVING se utiliza para filtrar grupos de registros creados por la cláusula GROUP BY. Por esta razón, la cláusula HAVING debe seguir a la cláusula GROUP BY. Es similar a la cláusula WHERE que filtra la salida de SELECT, sólo que WHERE filtra registros individuales mientras que HAVING filtra grupos.

Las funciones agregadas como SUM(), MAX(), MIN(), AVG() y COUNT() son el objetivo de la cláusula HAVING.

Veámoslo en acción en los ejemplos.

SELECT SUM(salary), department_id 
FROM hr.emp_details_view
GROUP BY department_id
HAVING SUM(salary) > 10000; 
SUM(SALARY)DEPARTMENT_ID
3400090
1380060
12008100
2500080

El ejemplo anterior enumera los departamentos junto con la suma de todos los salarios de cada departamento. Sólo los department_idcuyos salarios totales son superiores a 10000 aparecen en la lista. Los que no cumplen la condición establecida por la cláusula HAVING se filtran.

Veamos otro ejemplo:

SELECT COUNT(employee_id), job_id, salary
FROM hr.employees
WHERE salary > 12000
GROUP BY job_id, salary
HAVING COUNT(employee_id) < 10; 
COUNT(EMPLOYEE_ID)JOB_IDSALARY
1SA_MAN14000
2AD_VP17000
1FI_MGR12008

Esta consulta lista el número de empleados para cada combinación de job_id y salario. La cláusula WHERE filtra los registros a los que tienen sueldos superiores a 12000. La cláusula GROUP BY, que sigue a WHERE, especifica la agrupación por columnas no agregadas job_id y salary. Por último, la cláusula HAVING especifica que el valor agregado COUNT(employee_id) debe ser inferior a 10.

Escenario III: La cláusula GROUP BY

WHERE con una cláusula GROUP BY

La cláusula WHERE debe colocarse siempre antes de GROUP BY. Esto se debe a que WHERE filtra filas individuales de datos, no grupos de filas. La cláusula GROUP BY toma las filas individuales del resultado del filtro a nivel de fila WHERE para crear grupos de filas.

He aquí un ejemplo:

SELECT job_id, SUM(salary) 
FROM hr.employees
WHERE manager_id IN (100, 101, 102, 103)
GROUP BY job_id; 
JOB_IDSUM(SALARY)
AD_VP34000
FI_MGR12008
IT_PROG13800
SA_MAN25000

La consulta anterior se evalúa de la siguiente manera:

  1. La cláusula WHERE filtra primero los registros con manager_id que no se encuentran en la lista que sigue al operador IN.
  2. A continuación, la cláusula GROUP BY agrupa por job_id los registros que han superado la condición WHERE.

La consulta calcula el total de los salarios de los empleados gestionados por cada uno de los responsables especificados (manager_ids 100, 101, 102, 103). Los salarios de los empleados que dependen de otros gerentes no se incluyen en el cálculo de este total.

HAVING con una cláusula GROUP BY

La cláusula GROUP BY se utiliza a menudo con las funciones de agregación. Crea valores de resumen para las columnas enumeradas en GROUP BY.

En cambio, HAVING siempre sigue a una cláusula GROUP BY, ya que HAVING trabaja con los grupos creados por GROUP BY.

Consideremos un caso de uso de HAVING en el que no se puede omitir la cláusula GROUP BY. Esto es así si hay columnas listadas en SELECT que no son utilizadas por las funciones de agregación, como department_id en el ejemplo siguiente. Estas columnas no agregadas deben figurar en GROUP BY para agrupar los datos.

SELECT avg(salary), department_id 
FROM hr.emp_details_view
GROUP BY department_id
HAVING avg(salary) < 15000; 
AVG(SALARY)DEPARTMENT_ID
285030
690060
12008100
1250080

Sin embargo, HAVING puede utilizarse sin acompañar a GROUP BY. Tenga en cuenta que en este caso, HAVING se aplica a toda la salida de SELECT, tratándola como un único grupo. El siguiente es un ejemplo:

SELECT round(avg(salary))
FROM hr.emp_details_view
HAVING avg(salary) < 11000; 
ROUND(AVG(SALARY))
10056

La consulta devuelve un único valor que contiene la media de todos los salarios. Observe que la cláusula HAVING pone un límite a este valor agregado. Si la media calculada fuera superior a 11000, la consulta no habría devuelto ningún registro.

El siguiente es un ejemplo de pseudoagregación, en el que se utilizan funciones de agregación en lugar de GROUP BY:

SELECT MIN(first_name), MIN(department_id), MAX(salary)
FROM hr.emp_details_view
HAVING MIN(salary) > 1000; 
MIN(FIRST_NAME)MIN(DEPARTMENT_ID)MAX(SALARY)
Alexander3017000

La función MIN() se utiliza aquí en las columnas first_name y department_id. No se llama en un uso típico de las funciones de agregación, sino con el propósito de evitar un GROUP BY. En este caso, la salida es un solo registro de datos que consiste en el valor mínimo de first_name, el valor mínimo de department_id, y el valor máximo de salario, cada uno de la tabla completa. Tenga en cuenta que estos 3 valores pueden provenir de 3 registros diferentes como ocurre en este ejemplo. Además, en este caso, si el salario mínimo de toda la tabla fuera 1000 o menos, la consulta no devolvería ningún registro.

Este ejemplo concreto es un poco artificial para poder mostrar algo más sencillo. Aunque es menos común que GROUP BY, la pseudoagregación puede utilizarse en consultas que tratan todas las filas incluidas como un solo grupo.

El uso de HAVING con o sin GROUP BY puede variar según la base de datos. Si desea obtener más información sobre la cláusula GROUP BY, visite el artículo Cláusula GROUP BY.

Escenario IV: Uso conjunto de las cláusulas WHERE y HAVING

La sintaxis para utilizar WHERE y HAVING en una consulta sigue el orden específico que se presenta a continuación:

  1. SELECT: selecciona las columnas de la base de datos que se enviarán al resultado.
  2. FROM: enumera las tablas que se utilizarán en la consulta.
  3. WHEREFiltra los registros individuales.
  4. GROUP BYagrupa los registros en función de la(s) columna(s) especificada(s).
  5. HAVINGfiltra los grupos definidos por GROUP BY.
  6. ORDER BY: ordena los registros de salida por la(s) columna(s) especificada(s).

Veamos un ejemplo que utiliza todo lo anterior.

SELECT country_id, city, MIN(salary), MAX(salary) 
FROM hr.emp_details_view
WHERE country_id IN ('US', 'UK')
GROUP BY country_id, city
HAVING MIN(salary) < 15000
ORDER BY country_id; 
COUNTRY_IDCITYMIN(SALARY)MAX(SALARY)
UKOxford1100014000
USSeattle280017000
USSouthlake48009000

La consulta obtiene el mínimo salary y el máximo salary para cada combinación de city y country_id, esta última limitada a EE.UU. y Reino Unido sólo por la condición WHERE.

Supongamos que tiene 10 registros con EE.UU. como country_id, y dentro de ellos, hay 5 registros con Nueva York como ciudad y otros 5 con Los Ángeles como ciudad. Al ejecutar la consulta anterior, estos 10 registros se convierten en 2 registros

  • un registro para EE.UU. y Nueva York, con sus salarios mínimos y máximos,
  • otro registro para EE.UU. y Los Ángeles, también con sus salarios mínimos y máximos.

Para más información sobre las diferencias entre WHERE y HAVING, visite el artículo sobre SQL HAVING vs. WHERE.

Escenario V: WHERE y HAVING con JOINs

El siguiente ejemplo da un paso más para utilizar un JOIN con WHERE y HAVING. Une los datos de dos tablas basándose en una columna común, employee_id. Las cláusulas WHERE y HAVING siguen el orden de ejecución que acabamos de comentar.

SELECT e.job_id, edv.location_id, SUM(e.salary)
FROM hr.employees e
JOIN hr.emp_details_view edv
ON e.employee_id=edv.employee_id
WHERE e.job_id IN ('IT_PROG', 'SA_MAN')
GROUP BY e.job_id, edv.location_id
HAVING SUM(e.salary) > 5000
ORDER BY e.job_id; 
JOB_IDLOCATION_IDSUM(E.SALARY)
IT_PROG140013800
SA_MAN250025000

Este ejemplo selecciona la columna job_id de la tabla hr.employeela columna location_id de la tabla hr.emp_details_viewy el total de los salarios para cada combinación de job_id y location_id. La condición para unir las tablas aparece en el operador ON. Esta condición se basa en la columna común, employee_id.

A continuación, la cláusula WHERE filtra los registros a sólo job_ids que sean IT_PROG o SA_MAN. La cláusula GROUP BY agrupa los registros por la combinación de las columnas job_id y location_id. Por último, la cláusula HAVING filtra el valor agregado SUM(e.salary) a los mayores de 5000.

La cláusula ORDER BY ordena los registros de salida (en orden ascendente por defecto) por la columna job_id. Para más información sobre las cláusulas GROUP BY y ORDER BY, visite el artículo sobre GROUP BY vs. ORDER BY.

Diferencia entre las cláusulas WHERE y HAVING en SQL

Aquí hay un resumen a simple vista de las diferencias entre las cláusulas WHERE y HAVING en SQL:

WHERE clauseHAVING clause
FilteringFilters individual rows fetched by SELECT, i.e., the output of the SELECT commandFilters groups of rows created by GROUP BY, i.e., the output of the GROUP BY clause
ConditionsCannot have conditions containing aggregate functionsFocuses on conditions containing aggregate functions
SyntaxWHERE comes before GROUP BY in the syntaxHAVING comes after GROUP BY in the syntax
Order of evaluationWHERE clause is evaluated before GROUP BY in the process flow, before aggregation is performedHAVING clause is evaluated after GROUP BY in the process flow, after aggregation is performed
With/without GROUP BYCan be used with or without GROUP BY, since WHERE and GROUP BY are not relatedShould be used with GROUP BY; otherwise, the output of SELECT is treated as one group

WHERE y HAVING son una gran parte del aprendizaje de SQL. Consulte el SQL para principiantes curso para ayudarte a desarrollar tus habilidades en SQL.