GROUP BY, HAVING and ORDER BY in SQL Server
GROUP BY -> make groups (one row per group)
HAVING -> filter the GROUPS
ORDER BY -> sort the result
WHERE vs HAVING
WHERE -> filters ROWS (before grouping)
HAVING -> filters GROUPS (after grouping, can use SUM, COUNT ...)
Order of writing a query:
SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY
This lesson creates its own small tables, so you can run it on any SQL Server database. The Output under each query is the real result.
IF DB_ID('SqlBasicsDb') IS NULL
CREATE DATABASE SqlBasicsDb;
GO
USE SqlBasicsDb;
GO
DROP TABLE IF EXISTS Staff;
GO
CREATE TABLE Staff
(
EmpID INT PRIMARY KEY,
Name VARCHAR(50),
Department VARCHAR(30),
City VARCHAR(30),
Gender CHAR(1),
Salary INT
);
INSERT INTO Staff VALUES
(1, 'Ravi', 'IT', 'Hyderabad', 'M', 50000),
(2, 'Sita', 'HR', 'Chennai', 'F', 40000),
(3, 'John', 'IT', 'Bengaluru', 'M', 60000),
(4, 'Meena', 'Sales', 'Hyderabad', 'F', 35000),
(5, 'Arjun', 'Sales', 'Mumbai', 'M', 45000),
(6, 'Pooja', 'IT', 'Hyderabad', 'F', 55000),
(7, 'Kiran', 'HR', 'Mumbai', 'M', 38000),
(8, 'Divya', 'Finance', 'Chennai', 'F', 52000),
(9, 'Rahul', 'Finance', 'Bengaluru', 'M', 48000),
(10, 'Neha', 'IT', 'Hyderabad', 'F', 65000);
GO
SELECT * FROM Staff;
-- =============================================
-- ORDER BY
-- =============================================
Messages(10 rows affected)
Output
| EmpID | Name | Department | City | Gender | Salary |
| 1 | Ravi | IT | Hyderabad | M | 50000 |
| 2 | Sita | HR | Chennai | F | 40000 |
| 3 | John | IT | Bengaluru | M | 60000 |
| 4 | Meena | Sales | Hyderabad | F | 35000 |
| 5 | Arjun | Sales | Mumbai | M | 45000 |
| 6 | Pooja | IT | Hyderabad | F | 55000 |
| 7 | Kiran | HR | Mumbai | M | 38000 |
| 8 | Divya | Finance | Chennai | F | 52000 |
| 9 | Rahul | Finance | Bengaluru | M | 48000 |
| 10 | Neha | IT | Hyderabad | F | 65000 |
Sort by one column
SELECT Name, Salary FROM Staff ORDER BY Salary; -- low to high
SELECT Name, Salary FROM Staff ORDER BY Salary DESC; -- high to low
Output
| Name | Salary |
| Meena | 35000 |
| Kiran | 38000 |
| Sita | 40000 |
| Arjun | 45000 |
| Rahul | 48000 |
| Ravi | 50000 |
| Divya | 52000 |
| Pooja | 55000 |
| John | 60000 |
| Neha | 65000 |
Output
| Name | Salary |
| Neha | 65000 |
| John | 60000 |
| Pooja | 55000 |
| Divya | 52000 |
| Ravi | 50000 |
| Rahul | 48000 |
| Arjun | 45000 |
| Sita | 40000 |
| Kiran | 38000 |
| Meena | 35000 |
Sort by two columns (first Department, then Salary inside it)
SELECT Name, Department, Salary
FROM Staff
ORDER BY Department ASC, Salary DESC;
-- =============================================
-- GROUP BY
-- =============================================
Output
| Name | Department | Salary |
| Divya | Finance | 52000 |
| Rahul | Finance | 48000 |
| Sita | HR | 40000 |
| Kiran | HR | 38000 |
| Neha | IT | 65000 |
| John | IT | 60000 |
| Pooja | IT | 55000 |
| Ravi | IT | 50000 |
| Arjun | Sales | 45000 |
| Meena | Sales | 35000 |
Number of employees in each department
SELECT Department, COUNT(*) AS Employees
FROM Staff
GROUP BY Department;
Output
| Department | Employees |
| Finance | 2 |
| HR | 2 |
| IT | 4 |
| Sales | 2 |
Total and average salary in each department
SELECT Department,
SUM(Salary) AS TotalSalary,
AVG(Salary) AS AverageSalary
FROM Staff
GROUP BY Department;
Output
| Department | TotalSalary | AverageSalary |
| Finance | 100000 | 50000 |
| HR | 78000 | 39000 |
| IT | 230000 | 57500 |
| Sales | 80000 | 40000 |
Highest and lowest salary in each city
SELECT City, MAX(Salary) AS Highest, MIN(Salary) AS Lowest
FROM Staff
GROUP BY City;
Output
| City | Highest | Lowest |
| Bengaluru | 60000 | 48000 |
| Chennai | 52000 | 40000 |
| Hyderabad | 65000 | 35000 |
| Mumbai | 45000 | 38000 |
Group by Two columns (department + gender)
SELECT Department, Gender, COUNT(*) AS Employees
FROM Staff
GROUP BY Department, Gender;
Output
| Department | Gender | Employees |
| Finance | F | 1 |
| HR | F | 1 |
| IT | F | 2 |
| Sales | F | 1 |
| Finance | M | 1 |
| HR | M | 1 |
| IT | M | 2 |
| Sales | M | 1 |
GROUP BY + ORDER BY (biggest department first)
SELECT Department, COUNT(*) AS Employees
FROM Staff
GROUP BY Department
ORDER BY Employees DESC;
Output
| Department | Employees |
| IT | 4 |
| Sales | 2 |
| Finance | 2 |
| HR | 2 |
WHERE + GROUP BY (only employees from Hyderabad, then group)
SELECT Department, COUNT(*) AS Employees
FROM Staff
WHERE City = 'Hyderabad'
GROUP BY Department;
-- RULE: every column in SELECT must be inside GROUP BY
-- or inside an aggregate function (COUNT, SUM ...).
-- This gives an ERROR because Name is not grouped:
-- SELECT Department, Name, COUNT(*) FROM Staff GROUP BY Department;
-- =============================================
-- HAVING
-- =============================================
Output
| Department | Employees |
| IT | 3 |
| Sales | 1 |
Departments with more than 2 employees
SELECT Department, COUNT(*) AS Employees
FROM Staff
GROUP BY Department
HAVING COUNT(*) > 2;
Departments where the average salary is above 45000
SELECT Department, AVG(Salary) AS AverageSalary
FROM Staff
GROUP BY Department
HAVING AVG(Salary) > 45000;
Output
| Department | AverageSalary |
| Finance | 50000 |
| IT | 57500 |
Cities where total salary is at least 1,00,000
SELECT City, SUM(Salary) AS TotalSalary
FROM Staff
GROUP BY City
HAVING SUM(Salary) >= 100000;
-- =============================================
-- ALL TOGETHER : WHERE + GROUP BY + HAVING + ORDER BY
-- =============================================
Output
| City | TotalSalary |
| Bengaluru | 108000 |
| Hyderabad | 205000 |
Among employees earning more than 40000:
show departments with at least 2 such people, highest total salary first
SELECT Department,
COUNT(*) AS Employees,
SUM(Salary) AS TotalSalary
FROM Staff
WHERE Salary > 40000 -- 1. filter rows
GROUP BY Department -- 2. make groups
HAVING COUNT(*) >= 2 -- 3. filter groups
ORDER BY TotalSalary DESC; -- 4. sort
Output
| Department | Employees | TotalSalary |
| IT | 4 | 230000 |
| Finance | 2 | 100000 |