MySQL GROUP BY and HAVING Clause with Examples

โšก Smart Summary

SQL GROUP BY and HAVING clauses turn detailed rows into summary reports. GROUP BY collapses rows that share the same values into one row per group, while HAVING filters those groups after aggregate functions such as COUNT have been applied.

  • ๐Ÿ“Š Core Purpose: GROUP BY groups rows with identical values and returns a single row for every grouped item.
  • ๐Ÿงฉ Single Column Grouping: Grouping the members table on gender collapses nine rows into two, one for Female and one for Male.
  • ๐Ÿ”— Multiple Column Grouping: Grouping on two columns treats a row as unique when either value differs, so only exact duplicates collapse.
  • ๐Ÿงฎ Aggregate Pairing: COUNT, SUM, AVG, MIN, and MAX calculate one value per group, which produces the summary report.
  • ๐Ÿšฆ HAVING Versus WHERE: WHERE filters rows before grouping, HAVING filters the groups afterwards, and only HAVING accepts aggregate results.
  • โš ๏ธ Strict Mode Caution: Under ONLY_FULL_GROUP_BY, every selected column must be grouped or wrapped in an aggregate function.

SQL GROUP BY and HAVING Clause

What is the SQL GROUP BY Clause?

The GROUP BY clause is a SQL command that is used to group rows that have the same values. It is written inside the SELECT statement, and it is normally used together with aggregate functions to produce summary reports from the database.

That is what it does: it summarizes data held in the database. Queries that contain the GROUP BY clause are called grouped queries, and they return a single row for every grouped item.

SQL GROUP BY Syntax

Now that the purpose of the clause is clear, look at the syntax of a basic grouped query.

SELECT statements... GROUP BY column_name1[, column_name2, ...] [HAVING condition];

HERE

  • SELECT statements…” is the standard SQL SELECT command query.
  • GROUP BY column_name1” is the clause that performs the grouping based on column_name1.
  • [, column_name2, …]” is optional and represents other column names when the grouping is done on more than one column.
  • [HAVING condition]” is optional and is used to restrict the rows affected by the GROUP BY clause. It is similar to the WHERE clause, except that it is applied after the grouping.

Grouping Using a Single Column

The quickest way to see the effect of the SQL GROUP BY clause is to compare an ungrouped query with a grouped one. Start with a simple query that returns every gender entry in the members table.

SELECT `gender` FROM `members`;
gender
Female
Female
Male
Female
Male
Male
Male
Male
Male

Nine rows are returned, and every value is repeated. Suppose we want the unique values for gender instead. The query below adds the GROUP BY clause.

SELECT `gender` FROM `members` GROUP BY `gender`;

Executing the above script in MySQL Workbench against the myflixdb gives us the following results.

gender
Female
Male

Note that only two rows have been returned, because the table holds only two gender types. The GROUP BY clause grouped all the “Male” members together and returned a single row for them, and it did the same with the “Female” members.

Grouping Using Multiple Columns

Grouping on one column is often too coarse for a real report. GROUP BY accepts a comma-separated list of columns, and the combination of their values defines each group.

Suppose that we want a list of movie category_id values and the corresponding years in which the movies were released. Observe the output of this simple query first.

SELECT `category_id`, `year_released` FROM `movies`;
category_id year_released
1 2011
2 2008
NULL 2008
NULL 2010
8 2007
6 2007
6 2007
8 2005
NULL 2012
7 1920
8 NULL
8 1920

The highlighted rows show that the result contains duplicates. Executing the same query with GROUP BY removes them.

SELECT `category_id`, `year_released` FROM `movies` GROUP BY `category_id`, `year_released`;

Executing the above script in MySQL Workbench against the myflixdb gives us the following results shown below.

category_id year_released
NULL 2008
NULL 2010
NULL 2012
1 2011
2 2008
6 2007
7 1920
8 1920
8 2005
8 2007

The GROUP BY clause operates on both category_id and year_released to identify unique rows. The two duplicate rows for category 6 in 2007 collapsed into one.

Rule of thumb: if the category id is the same but the year released is different, the row is treated as unique. If the category id and the year released are the same for more than one row, the rows are duplicates and only one of them is shown.

Grouping and Aggregate Functions

Removing duplicates is useful, but the real power of grouping appears when it is paired with aggregate functions. An aggregate function calculates one value for each group: COUNT counts rows, SUM adds values, and AVG, MIN, and MAX describe the spread.

Suppose we want the total number of male and female members in the database. The script below does that.

SELECT `gender`, COUNT(`membership_number`) FROM `members` GROUP BY `gender`;

Executing the above script in MySQL Workbench against the myflixdb gives us the following results.

gender COUNT(`membership_number`)
Female 3
Male 6

The rows are grouped by every unique gender value, and the number of rows inside each group is counted by the COUNT aggregate function. The nine member records collapse into two summary rows.

Restricting Query Results Using the HAVING Clause

Groupings are not always wanted for every row in a table. Sometimes the report must be restricted to a given criterion, and that is the job of the HAVING clause.

Suppose we want to know all the release years for movie category id 8. The script below achieves that result.

SELECT * FROM `movies` GROUP BY `category_id`, `year_released` HAVING `category_id` = 8;

Executing the above script in MySQL Workbench against the myflixdb gives us the following results shown below.

movie_id title director year_released category_id
9 Honey mooners John Schultz 2005 8
5 Daddy’s Little Girls NULL 2007 8

Only the movies with category id 8 have been kept by the HAVING condition.

Warning: MySQL 5.7 and later enable the ONLY_FULL_GROUP_BY mode by default, and under that mode SELECT * with a GROUP BY clause is rejected, because movie_id, title, and director are neither grouped nor aggregated. In production, name the grouped columns explicitly, for example SELECT category_id, year_released FROM movies GROUP BY category_id, year_released HAVING category_id = 8;

WHERE vs HAVING vs GROUP BY vs ORDER BY

Beginners frequently mix these four clauses, because all of them shape the result set. The difference lies in when MySQL applies them: WHERE runs before the rows are grouped, HAVING runs after, and ORDER BY runs last of all.

Clause What it does When it runs Accepts aggregate functions
WHERE Filters individual rows before any grouping. Before GROUP BY No
GROUP BY Collapses rows sharing the same values into one row per group. After WHERE Not applicable
HAVING Filters the groups produced by GROUP BY. After GROUP BY Yes, for example HAVING COUNT(*) > 2
ORDER BY Sorts the rows that survive the previous clauses. Last Yes, an aggregate alias can be sorted

The practical consequence is a performance one. Filtering with WHERE removes rows before the grouping work starts, so a condition that does not depend on an aggregate result belongs in WHERE rather than HAVING.

FAQs

Yes. GROUP BY on its own returns one row per unique value, which removes duplicates in much the same way as SELECT DISTINCT. Aggregate functions are only required when each group needs a calculated figure.

The error appears when a selected column is neither listed in GROUP BY nor wrapped in an aggregate function. MySQL cannot decide which value of that column to show for the group, so it refuses the query.

COUNT(*) counts every row in the group. COUNT(column) counts only the rows where that column is not NULL, so the two figures differ whenever the column holds missing values.

Yes. AI assistants inside tools such as MySQL Workbench translate a request such as “members per gender” into a grouped query. Check the grouping columns yourself, because a wrong grouping produces totals that look plausible but are incorrect.

Often, yes. AI query assistants flag classic causes such as a JOIN that multiplies rows before grouping, or a filter placed in HAVING instead of WHERE. The final judgement still belongs to the person who knows the data.

Summarize this post with: