SQL GROUP BY Clause
In this lesson, you will learn how to group rows that have common values using SQL.
The GROUP BY Clause In SQL
The GROUP BY
clause can be used in conjunction with the aggregate functions to group the returned rows that have common values and then perform calculations based on those groups.
For example, to discover how many users you have in each country you can group the result based on the country column, then with the COUNT()
function you can display the number of users in each country.
Syntax
SELECT column_name(s) FROM table_name WHERE condition GROUP BY column_name(s) ORDER BY column_name(s);
The WHERE
clause is applied before grouping the result, but the ORDER BY
operator is applied after grouping the result. That's why the GROUP BY
clause is put in the middle.
Preparing A Database
Since you will learn how to group the retrieved rows, you must have a database table with some records having common data to practice grouping them. Execute the following query to create the database.
Example
-- If there is already a database named "demo" delete it DROP DATABASE IF EXISTS demo; -- Create a new database called "demo" CREATE DATABASE demo; -- Select the "demo" database USE demo; -- Create a new table called "employees" inside the selected database CREATE TABLE employees ( id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50), gender VARCHAR(20), country VARCHAR(100), salary DECIMAL(7,2) ); -- Insert 10 records in the "employees" table -- We add null in the id column because its value will be generated automatically by the database server INSERT INTO employees VALUES (null, "Emily", "female", "Denmark", 800); INSERT INTO employees VALUES (null, "Stacy", "female", "Australia", 900); INSERT INTO employees VALUES (null, "John", "male", "Canada", 750); INSERT INTO employees VALUES (null, "Lana", "female", "Germany", 1000); INSERT INTO employees VALUES (null, "Ronaldo", "male", "Brazil", 845); INSERT INTO employees VALUES (null, "Louise", "female", "France", 700); INSERT INTO employees VALUES (null, "James", "male", "Canada", 920); INSERT INTO employees VALUES (null, "Maria", "female", "Brazil", 850); INSERT INTO employees VALUES (null, "Mia", "female", "Canada", 780); INSERT INTO employees VALUES (null, "Luke", "male", "Japan", 880);
This is the employees table that we created in the demo database.
id | username | gender | country | salary |
---|---|---|---|---|
1 | Emily | female | Denmark | 800.00 |
2 | Stacy | female | Australia | 900.00 |
3 | John | male | Canada | 750.00 |
4 | Lana | female | Germany | 1000.00 |
5 | Ronaldo | male | Brazil | 845.00 |
6 | Louise | female | France | 700.00 |
7 | James | male | Canada | 920.00 |
8 | Maria | female | Brazil | 850.00 |
9 | Mia | female | Canada | 780.00 |
10 | Luke | male | Japan | 880.00 |
Group By Single Column
The following query groups all employees based on their country and returns the country. So It will returns unique values.
Note: It's better to use the DISTINCT
keyword if your purpose is just to return unique values.
Example 1
SELECT country FROM employees GROUP BY country;
Same query can be written as follows.
SELECT DISTINCT country FROM employees;
Output
country |
---|
Denmark |
Australia |
Canada |
Germany |
Brazil |
France |
Japan |
The following query returns the number of employees in each country.
We group the rows that have the same country, and then we count the number of rows in each group.
Example 2
SELECT country AS 'Country', COUNT(*) AS 'Total Employees' FROM employees GROUP BY country;
Output
Country | Total Employees |
---|---|
Denmark | 1 |
Australia | 1 |
Canada | 3 |
Germany | 1 |
Brazil | 2 |
France | 1 |
Japan | 1 |
The following query returns the number of employees in each country in descending order.
We group the rows that have the same country, and then we count the number of rows in each group, and then we sort the result in descending order.
Example 3
SELECT country AS 'Country', COUNT(*) AS 'Total_Employees' FROM employees GROUP BY country ORDER BY Total_Employees DESC;
Output
Country | Total_Employees |
---|---|
Canada | 3 |
Brazil | 2 |
Denmark | 1 |
Australia | 1 |
Germany | 1 |
France | 1 |
Japan | 1 |
The following query returns the number of male employees in each country in descending order.
We group the rows that have the same country, and then we keep the rows where the gender is male, and then we count the number of rows in each group, and then we sort the result in descending order.
Example 4
SELECT country AS 'Country', COUNT(*) AS 'Total_Male_Employees' FROM employees WHERE gender = 'male' GROUP BY country ORDER BY Total_Male_Employees DESC;
Output
Country | Total_Male_Employees |
---|---|
Canada | 2 |
Brazil | 1 |
Japan | 1 |
The following query returns the number of male and female employees.
We group the rows that have the same gender, and then we count the number of rows in each group.
Example 5
SELECT gender AS 'Gender', COUNT(*) AS 'Total Employees' FROM employees GROUP BY country;
Output
Gender | Total Employees |
---|---|
Female | 6 |
Male | 4 |
The following query returns the average salary in each country.
We group the rows that have the same country, and then we calculate the average salary in each group.
Example 6
SELECT country AS 'Country', AVG(salary) AS 'Average Salaries' FROM employees GROUP BY country;
Output
Country | Average Salaries |
---|---|
Canada | 800.000000 |
Brazil | 900.000000 |
Denmark | 816.666667 |
Australia | 1000.000000 |
Germany | 847.500000 |
France | 700.000000 |
Japan | 880.000000 |
The following query returns the number of employees and the total salaries in each country.
We group the rows that have the same country, and then we count the number and the sum of salaries in each group.
Example 7
SELECT country AS 'Country', COUNT(id) AS 'Total Employees', SUM(salary) AS 'Total Salaries' FROM employees GROUP BY country;
Output
Country | Total Employees | Total Salaries |
---|---|---|
Denmark | 1 | 800.00 |
Australia | 1 | 900.00 |
Canada | 3 | 2450.00 |
Germany | 1 | 1000.00 |
Brazil | 2 | 1695.00 |
France | 1 | 700.00 |
Japan | 1 | 880.00 |
Group By Multiple Column
The following query returns the number of male and female employees in each country.
First, we group the rows that have the same country, and then we group them again based on their genders, and then we count the number of rows in each gender. At the end, we sort the results based on country names.
Example
SELECT country AS 'Country', gender AS 'Gender', COUNT(id) AS 'Total Employees' FROM employees GROUP BY country, gender ORDER BY country;
Output
Country | Gender | Total Employees |
---|---|---|
Australia | female | 1 |
Brazil | female | 1 |
Brazil | male | 1 |
Canada | female | 1 |
Canada | male | 2 |
Denmark | female | 1 |
France | female | 1 |
Germany | female | 1 |
Japan | male | 1 |
Note: Brazil and Canada are repeated because they have male and female employees.
Group By Exercise
1- Write a query that creates a new database called "training".
Note: you should recreate it if it already exists.
2- Write a query that creates a table called "users" and check whether it exists or not. the table should contain the following fields:
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT
username VARCHAR(50)
role VARCHAR(20)
join_date DATE
3- Write a query that inserts 10 rows in the "users" table.
Note: roles should be "admin", "editor", "writer" or "subscriber".
4- Write a query that returns how many users are in each role in the "users" table in ascending order.
Solution
DROP DATABASE IF EXISTS training; CREATE DATABASE training; USE training; CREATE TABLE IF NOT EXISTS users ( id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50), role VARCHAR(20), join_date DATE ); INSERT INTO users (username, role, join_date) VALUES ('Masha', 'admin', '2020-02-01'), ('Adam', 'editor', '2020-02-01'), ('Moly', 'subscriber', '2020-02-03'), ('Sandra', 'writer', '2020-02-04'), ('Lili', 'subscriber', '2020-02-04'), ('Moses', 'subscriber', '2020-02-04'), ('Jack', 'subscriber', '2020-02-07'), ('Ali', 'editor', '2020-02-10'), ('Alfred', 'writer', '2020-02-13'), ('Mohamed', 'writer', '2020-02-15'); SELECT role As 'Roles', COUNT(*) AS 'Total_Users' FROM users GROUP BY role ORDER BY Total_Users;