SQL: Filtering Aggregations with HAVING

The HAVING clause in SQL is an indispensable tool for analytics engineers, enabling filtering based on aggregated data. Unlike the WHERE clause, which filters rows before aggregation, HAVING filters grouped data after applying aggregate functions such as COUNT, SUM, AVG, MIN, and MAX.

This guide, “SQL: Filtering Aggregations With HAVING,” will enhance your ability to refine aggregated results, providing clearer insights from complex datasets. Mastering this clause is crucial for effectively summarizing data and pinpointing significant aggregated patterns, whether identifying high-performing customer segments, product categories meeting specific criteria, or departments with particular salary characteristics.

🚀 Jump Right to Exercise Tasks: SQL: Filtering Aggregations With HAVING

Filtering Groups with HAVING and COUNT

The HAVING clause filters groups based on aggregated counts, enabling the identification of groups that meet numeric criteria. For example, identifying departments within an organization that have a significant number of employees helps managers make informed staffing decisions and resource allocations.

Practical Example

SELECT department, COUNT(employee_id) AS num_employees
FROM staff
GROUP BY department
HAVING COUNT(employee_id) > 5;

Example Solution:

department | num_employees
-----------|--------------
Sales      | 12
Marketing  | 8

Key Takeaways:

  • Filter effectively after aggregation.
  • Highlight groups meeting specific numeric criteria.

Using HAVING with SUM and AVG

HAVING is particularly effective for filtering grouped data based on sum totals or averages. For instance, businesses can quickly identify categories of products that meet certain pricing or revenue thresholds, streamlining strategic decision-making and inventory management.

Practical Example

SELECT category, AVG(price) AS average_price
FROM items
GROUP BY category
HAVING AVG(price) > 50;

Example Solution:

category     | average_price
-------------|--------------
Electronics  | 120.50
Furniture    | 85.75

Key Takeaways:

  • Ideal for numeric threshold filtering.
  • Enhance data-driven insights.

Combining WHERE and HAVING Clauses

Combining WHERE and HAVING clauses provides a powerful method for initial row-level filtering followed by aggregate-based group filtering. Analysts can focus on specific subsets of data, such as sales from a particular year, and further refine these results based on aggregate totals, enhancing overall analytical clarity and precision.

Practical Example

SELECT region, SUM(sales) AS total_sales
FROM transactions
WHERE year = 2023
GROUP BY region
HAVING SUM(sales) > 100000;

Example Solution:

region | total_sales
-------|------------
North  | 150000
West   | 125000

Key Takeaways:

  • Precise initial and subsequent filtering.
  • Effective for targeted data analytics.

HAVING with Multiple Aggregations

Using multiple aggregates in HAVING conditions enables comprehensive data analysis. For example, businesses can filter suppliers based on both product counts and average costs, providing valuable insights for procurement and vendor management.

Practical Example

SELECT supplier_id,
       COUNT(product_id) AS total_products,
       AVG(unit_cost) AS avg_cost
FROM supplies
GROUP BY supplier_id
HAVING COUNT(product_id) > 10 AND AVG(unit_cost) < 50;

Example Solution:

supplier_id | total_products | avg_cost
------------|----------------|---------
S104        | 15             | 45.00
S110        | 12             | 47.50

Key Takeaways:

  • Complex, multi-condition group filtering.
  • Ideal for detailed, criterion-based analytics.

What You’ll Gain from Completing This Exercise

By mastering the use of HAVING, you will refine your skills in advanced SQL queries, enhancing your ability to summarize data clearly and effectively for informed decision-making.

How to Complete the Exercise Tasks

  • Write your SQL code: Enter queries into the provided editor.
  • Run your query: Click "Run" to see your results.
  • Check your solution: Validate your query results by clicking "Check Answer".
  • Reset the editor: Clear and retry using "Reset".

Earn XP, Unlock Rewards, and Track Progress!

Log in to gain XP, unlock rewards, and automatically track progress as you build your SQL and analytics expertise!

Schema Information

Column Name Data Type Description
customer_id INTEGER Unique identifier for each customer
first_name TEXT Customer's first name
last_name TEXT Customer's last name
email TEXT Customer's email address
city TEXT City of residence
join_date DATE Date the customer joined
phone TEXT Customer's phone number
Column Name Data Type Description
city_id INTEGER Unique identifier for each city
city_name TEXT Name of the city
state TEXT State where the city is located
Column Name Data Type Description
order_id INTEGER Unique identifier for each order
customer_id INTEGER ID of the customer who placed the order
employee_id INTEGER ID of the employee who handled the order
order_date DATE Date when the order was placed
status TEXT Order status (e.g., Shipped, Cancelled, Pending)
total_amount REAL Total amount for the order
Column Name Data Type Description
order_id INTEGER Associated order identifier
product_id INTEGER Identifier for the product
quantity INTEGER Quantity of the product in the order
unit_price REAL Price per unit of the product
Column Name Data Type Description
product_id INTEGER Unique identifier for each product
product_name TEXT Name of the product
category_id INTEGER Category to which the product belongs
unit_price REAL Price per unit of the product
stock_quantity INTEGER Quantity of items available in stock
creation_date DATE Date the product was added to the system
Column Name Data Type Description
supplier_id INTEGER Unique identifier for each supplier
supplier_name TEXT Name of the supplier
city TEXT City where the supplier is located
products_supplied TEXT List of product IDs supplied (e.g., [90,91,33,121])
Column Name Data Type Description
movement_id INTEGER Unique identifier for each movement record
product_id INTEGER Product being moved
change_quantity INTEGER Change in quantity (+/-)
movement_date DATE Date of the inventory movement
reason TEXT Explanation (Sale, Restock, Adjustment, etc.)
Column Name Data Type Description
employee_id INTEGER Unique identifier for each employee
first_name TEXT Employee's first name
last_name TEXT Employee's last name
department_id INTEGER Identifier of the department
hire_date DATE Date the employee was hired
salary INTEGER Employee's salary
Column Name Data Type Description
department_id INTEGER Unique identifier for each department
department_name TEXT Name of the department
location TEXT Location of the department
Column Name Data Type Description
shipping_id INTEGER Unique identifier for each shipping record
order_id INTEGER Order to which the shipping address belongs
address TEXT Street address for shipping
city TEXT Shipping city
state TEXT Shipping state
postal_code INTEGER Shipping postal/ZIP code
Column Name Data Type Description
category_id INTEGER Unique identifier for each category
category_name TEXT Name of the category

This wealth of interconnected data will allow you to work through exercises that involve basic inner joins, and more—building not just technical SQL proficiency, but also a deeper understanding of data relationships and reporting in a realistic business context.

SQL: Filtering Aggregations with HAVING

SQL: Filtering Aggregations with HAVING

Ask Tutor
Tutor Chat