When working with SQL, one of the most common tasks is grouping data. Whether you’re calculating sums, averages, or counts, grouping is essential for analyzing large datasets. However, after grouping data, you may want to filter the results based on aggregate values. That’s where the HAVING clause comes in.
The HAVING clause is used to filter records after the GROUP BY operation has been applied. It is typically used with aggregate functions like COUNT()
, SUM()
, AVG()
, MAX()
, and MIN()
to filter groups of data based on the results of these functions.
In simple terms, HAVING allows you to filter grouped data, whereas the WHERE clause filters data before it is grouped.
The general syntax for using the HAVING clause is as follows:
SELECT column1, column2, aggregate_function(column3)
FROM table_name
WHERE condition
GROUP BY column1, column2
HAVING aggregate_condition;
While both WHERE and HAVING clauses filter data, they work at different stages of query execution:
Key Difference:
-- WHERE Clause (Filters rows before grouping)
SELECT department, AVG(salary)
FROM employees
WHERE salary > 50000
GROUP BY department;
-- HAVING Clause (Filters after aggregation)
SELECT department, AVG(salary)
FROM employees
GROUP BY department
HAVING AVG(salary) > 50000;
Let’s use the HAVING clause in a simple example. Suppose you have an orders
table with columns order_id
, customer_id
, and order_amount
. You want to find customers who have placed more than 3 orders, with each order amount exceeding $100.
SELECT customer_id, COUNT(order_id) AS total_orders, SUM(order_amount) AS total_spent
FROM orders
GROUP BY customer_id
HAVING COUNT(order_id) > 3 AND SUM(order_amount) > 300;
Explanation:
customer_id
.You can use the HAVING clause with multiple aggregate functions to apply more complex filters to grouped data.
SELECT employee_id, COUNT(order_id) AS total_orders, SUM(order_amount) AS total_sales
FROM orders
GROUP BY employee_id
HAVING COUNT(order_id) > 5 AND SUM(order_amount) > 1000;
Explanation:
COUNT(order_id)
) and sums the order_amount
(SUM(order_amount)
) for each employee.You can combine the WHERE and HAVING clauses in the same query to filter data at both stages of the query execution.
SELECT department_id, AVG(salary) AS average_salary
FROM employees
WHERE hire_date > '2020-01-01'
GROUP BY department_id
HAVING AVG(salary) > 50000;
Explanation:
department_id
.The HAVING clause allows you to apply complex conditions involving aggregate functions. You can also combine logical operators like AND
, OR
, and NOT
for more advanced filtering.
SELECT department_id, COUNT(employee_id) AS num_employees, AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id
HAVING COUNT(employee_id) > 10 AND AVG(salary) < 60000;
Explanation:
COUNT(employee_id)
) and the average salary (AVG(salary)
).COUNT()
, SUM()
, etc.). For non-aggregated columns, use WHERE instead.