Home/Curriculum/Lesson 10
Beginner4 min read

Grouping Data with GROUP BY and HAVING

Understanding Data Grouping

In the previous lesson, you learned how to use aggregate functions to summarize an entire dataset into a single value. However, analytical tasks frequently require you to summarize data into specific logical subsets rather than calculating one grand total for the entire table. For example, instead of finding the total number of products across your entire inventory, you might need to determine the total number of products within each specific product category.

To accomplish this type of segmented summarization, SQL provides the GROUP BY and HAVING clauses. These clauses allow you to partition your data into distinct groups and apply aggregate calculations independently to each of those partitions.

The GROUP BY Clause

The GROUP BY clause instructs the database system to collect data across multiple records and consolidate the results based on the unique values found in one or more columns. It operates in direct conjunction with aggregate functions like COUNT(), SUM(), or AVG().

POSTGRESQL CODE SNIPPET
SELECT category, COUNT(*) AS total_products
FROM products
GROUP BY category;

In this statement, the database evaluates the category column, creates a distinct group for each unique category name it finds, and then applies the COUNT(*) function to each individual group. The final output displays every unique category alongside its specific product count.

Grouping by Multiple Columns

You are not limited to grouping by a single column. By listing multiple columns in the GROUP BY clause, separated by commas, you can create highly granular, nested summaries. The database will evaluate every unique combination of the specified columns and generate an aggregated summary row for each distinct pairing.

POSTGRESQL CODE SNIPPET
SELECT department, job_title, AVG(salary) AS average_salary
FROM employees
GROUP BY department, job_title;

In this scenario, the database first groups the data by department. Within each department, it creates subgroups for every distinct job title. The AVG() function then calculates the average salary for that specific job title within that specific department, providing a deep level of organizational insight.

Grouping Rules

When utilizing the GROUP BY clause, you must adhere to several strict syntax rules to avoid execution errors:

  • Every standard column listed in your SELECT clause must also be explicitly listed in the GROUP BY clause. You cannot select an unaggregated column without telling the database how to group it.
  • If you group by multiple columns, the database evaluates the unique combinations of all those columns to form the final groups.
  • The GROUP BY clause operates within a strict sequence. It must always follow the WHERE clause and must precede the ORDER BY clause.
  • If a column specified in the GROUP BY clause contains NULL values, the database will collect all of those NULL values into a single, consolidated group.

Filtering Groups with HAVING

Just as you filter individual rows using the WHERE clause, you will often need to filter aggregated groups. For instance, you might want to return only the product categories that contain more than one item. You cannot use the WHERE clause for this task because the WHERE clause filters raw data before the grouping operation occurs.

To filter data based on aggregated group values, you must use the HAVING clause.

POSTGRESQL CODE SNIPPET
SELECT category, COUNT(*) AS total_products
FROM products
GROUP BY category
HAVING COUNT(*) > 1;

The database groups the rows by category, counts the products within each category, and then applies the HAVING condition to exclude any entire group that does not meet the specified threshold.

Combining WHERE and HAVING

The WHERE and HAVING clauses serve different purposes, and you can combine them within a single query to achieve highly precise data retrieval.

POSTGRESQL CODE SNIPPET
SELECT category, COUNT(*) AS total_products
FROM products
WHERE price >= 20.00
GROUP BY category
HAVING COUNT(*) > 1;

In this query, the execution order is critical. First, the WHERE clause filters out any individual product priced under 20.00. Second, the GROUP BY clause takes the remaining expensive products and partitions them by category. Third, the aggregate function counts the items in those specific partitions. Finally, the HAVING clause filters out any category group that contains one or fewer products.

Apply these grouping and group-filtering techniques to categorize your data and extract highly specific summary metrics.

Advertisement