The HAVING clause in MySQL 8: A Practical Guide

Updated: January 26, 2024 By: Guest Contributor Post a comment

Overview

The HAVING clause in MySQL is an indispensable tool for database users who wish to filter query results after the aggregation operation. Unlike the WHERE clause, which filters rows before aggregation, the HAVING clause does this after the aggregation phase, which is particularly useful with GROUP BY statements.

Throughout this guide, we will explore the HAVING clause in MySQL 8 through multiple code examples, progressing from basic to advanced uses. Understanding the HAVING clause allows for more sophisticated queries and reports from your relational database systems.

Basics of the HAVING Clause

Example 1: Filtering Groups with HAVING
Let’s start with a simple example using a fictional ‘orders’ table.

SELECT employee_id, COUNT(*) AS order_count
FROM orders
GROUP BY employee_id
HAVING COUNT(*) > 5;

In this case, the query will return the ’employee_id’ and a count of orders for each employee, but only include those employees with more than five orders.

employee_idorder_count
18
26

Example 2: Combining WHERE and HAVING
The WHERE and HAVING clauses can be used in conjunction to filter data at different stages of the query process.

SELECT product_id, COUNT(*) AS total_sales
FROM sales
WHERE purchase_date >= '2021-01-01'
GROUP BY product_id
HAVING COUNT(*) > 10;

Here, the WHERE clause narrows down the sales records to those occurring after January 1st, 2021. After grouping by ‘product_id’, the HAVING clause filters out any groups with total sales of 10 or less.

Intermediate Uses of HAVING

Moving beyond the basics, we can apply HAVING with multiple aggregate functions and criteria.

SELECT customer_id, SUM(amount) as total_spent, AVG(amount) as average_spent
FROM transactions
GROUP BY customer_id
HAVING SUM(amount) > 500 AND AVG(amount) < 50;

This example filters customers by their total and average spending by using both functions in the HAVING clause.

Advanced HAVING Clause Examples

Advanced queries may involve subqueries, multiple layers of aggregation, and references to other tables.

SELECT author_id, COUNT(*) as total_books, AVG(page_count) as average_pages
FROM books
GROUP BY author_id
HAVING AVG(page_count) > (
    SELECT AVG(page_count)
    FROM books)
ORDER BY total_books DESC;

This advanced example compares each author’s average page count to the overall average and selects authors with larger-than-average books.

Complex JOINs with HAVING

JOINs in queries can significantly expand the capabilities of the HAVING clause:

SELECT companies.name, COUNT(employees.id) AS total_employees
FROM companies
JOIN employees ON companies.id = employees.company_id
GROUP BY companies.name
HAVING COUNT(employees.id) > (
  SELECT AVG(total_employees) FROM (
    SELECT COUNT(*) as total_employees
    FROM employees
    GROUP BY company_id
  ) AS subquery);

This complex query not only demonstrates a JOIN but also uses a subquery within the HAVING clause to filter companies by the number of employees exceeding the average across companies.

Conclusion

In this guide, we’ve journeyed from basic to advanced applications of the HAVING clause in MySQL 8. Understanding and utilizing the HAVING clause allows for much more nuanced data analysis and report generation — an essential skill in today’s data-driven world.