SQL Server · Querying

HAVING

WHERE filters rows; HAVING filters groups. Knowing the difference is a favourite interview question — and essential for reports.

Beginner 5 min Lesson 9 of 31Examples run on SQL Server 2022 and Azure SQL Database.
Before you start:GROUP BYJoins
0/31 in SQL Server
On this page · 5 min · Beginner

In simple words#

WHERE filters students before making piles; HAVING filters the piles afterwards: “only classes with more than 40 students.”

HAVING
Filter groups after GROUP BY.
WHERE vs HAVING
Rows before grouping vs groups after.
Typical use
HAVING COUNT(*) > 40.

Learning objectives#

  • Filter groups with HAVING
  • Explain WHERE vs HAVING
  • Know the logical processing order of a query
  • Combine joins, grouping and HAVING

Explanation#

HAVING is applied after GROUP BY, so it can use aggregate functions such as SUM and COUNT.

The logical order is: FROM/JOIN → WHERE → GROUP BY → HAVING → SELECT → ORDER BY. That is why WHERE cannot refer to aggregates or SELECT aliases.

Filter rows early in WHERE when you can — fewer rows to group means less work.

Syntax#

SQL Illustrative
SELECT col, SUM(x) FROM t
WHERE rowCondition
GROUP BY col
HAVING SUM(x) > 100;

Code example: Customers who spent more than ₹10,000#

SQLCustomers who spent more than ₹10,000 Illustrative
SELECT c.Name, SUM(oi.Qty * oi.UnitPrice) AS TotalSpent
FROM Customers AS c
JOIN Orders AS o      ON o.CustomerId = c.CustomerId
JOIN OrderItems AS oi ON oi.OrderId   = o.OrderId
WHERE o.Status <> 'Cancelled'
GROUP BY c.Name
HAVING SUM(oi.Qty * oi.UnitPrice) > 10000
ORDER BY TotalSpent DESC;
Expected output
Name  TotalSpent
Asha  56900.00
Ravi  12000.00

This example is illustrative. Copy or download it and run it in your own environment; the expected output is shown above.

Step-by-step explanation#

  1. The joins produce one row per order item with its customer.
  2. WHERE removes Meera’s cancelled order before grouping.
  3. GROUP BY totals each customer: Asha 56,400 + 500 = 56,900; Ravi 12,000.
  4. HAVING keeps only totals above 10,000.
  5. ORDER BY can use the alias TotalSpent because it runs after SELECT.

Real-world use case#

Loyalty programmes find “customers who spent over X this year” with exactly this pattern.

Common mistakes#

Using HAVING for row filters

HAVING Status <> 'Cancelled' works only if Status is grouped and is slower. Put row conditions in WHERE.

Using a SELECT alias in HAVING

HAVING TotalSpent > 10000 fails in SQL Server because HAVING runs before SELECT. Repeat the expression.

Best practices#

  • Row filters in WHERE, aggregate filters in HAVING
  • Repeat the aggregate expression in HAVING
  • Remember the logical processing order when debugging
  • Alias aggregate columns for readable output

Remember this#

WHERE filters rows; HAVING filters groups.

HAVING comes after GROUP BY.

HAVING COUNT(*) > 5 = groups with more than 5 rows.

Practice exercise#

Your task: List categories whose total product value is under 20,000.

Show solution
SQL Illustrative
SELECT Category, SUM(Price) AS Total FROM Products GROUP BY Category HAVING SUM(Price) < 20000;  -- Furniture 16500.00, Stationery 80.00

Mini quiz#

HAVING quizMultiple choice Question 1 of 2

Which clause can filter using COUNT(*)?

Score so far: 0/0

Want more? Practise all SQL Server MCQs · SQL Server interview questions

My notes#

Saved in this browser.

Progress is saved in this browser. Sign in to sync.