Menu

Earn Premium with Referrals

Invite your friends and earn Premium rewards through our referral program.

See how it works and start inviting friends.

SQL Aggregations and Grouping
DBMS

SQL Aggregations and Grouping

Master aggregate functions, GROUP BY, HAVING, and advanced grouping features like ROLLUP, CUBE, and GROUPING SETS.

SQL Aggregations and Grouping

Raw data is just a list of rows. To answer business questions — “What is the average order value?” or “Which city has the most customers?” — you need to aggregate.

Aggregation is the process of combining multiple rows into a single summary value.

This chapter covers aggregate functions, grouping, filtering groups, and advanced OLAP features.


Learning Objectives

After completing this chapter, you will be able to:

  • Use the five core aggregate functions.
  • Write GROUP BY queries correctly.
  • Filter groups with HAVING.
  • Use DISTINCT with aggregates.
  • Understand NULL behavior in aggregations.
  • Use GROUPING SETS, ROLLUP, and CUBE.
  • Answer interview questions about aggregation.

Sample Data

All examples use this Orders table:

CREATE TABLE Orders (
    Order_ID INT PRIMARY KEY,
    Customer VARCHAR(50),
    City VARCHAR(50),
    Product VARCHAR(50),
    Amount DECIMAL(10,2),
    Quantity INT,
    Order_Date DATE
);
Order_IDCustomerCityProductAmountQuantityOrder_Date
1RahulMumbaiLaptop5000012024-01-15
2PriyaDelhiPhone1500022024-01-16
3AmitMumbaiTablet2500012024-01-17
4RahulMumbaiPhone1500012024-02-01
5PriyaDelhiLaptop5000012024-02-05
6SnehaBangaloreTablet2500022024-02-10
7AmitMumbaiLaptop5000012024-03-01

Core Aggregate Functions

SQL provides five standard aggregate functions.

FunctionPurposeExample
COUNT()Counts number of rowsCOUNT(*) — total rows
SUM()Sums numeric valuesSUM(Amount)
AVG()Average of numeric valuesAVG(Amount)
MAX()Maximum valueMAX(Amount)
MIN()Minimum valueMIN(Amount)

COUNT Examples

-- Count all rows
SELECT COUNT(*) FROM Orders;  -- Result: 7

-- Count non-NULL values in a column
SELECT COUNT(Customer) FROM Orders;  -- Result: 7

-- Count distinct values
SELECT COUNT(DISTINCT Customer) FROM Orders;  -- Result: 4

SUM and AVG Examples

-- Total revenue
SELECT SUM(Amount) AS Total_Revenue FROM Orders;  -- 230000

-- Average order value
SELECT AVG(Amount) AS Avg_Order FROM Orders;  -- 32857.14

-- Average quantity per order
SELECT AVG(Quantity) AS Avg_Qty FROM Orders;  -- 1.29

MAX and MIN Examples

-- Highest and lowest order amounts
SELECT MAX(Amount) AS Highest, MIN(Amount) AS Lowest FROM Orders;
-- Result: Highest = 50000, Lowest = 15000

-- Most items in a single order
SELECT MAX(Quantity) FROM Orders;  -- 2

GROUP BY

GROUP BY groups rows that have the same values in specified columns. Aggregates are applied to each group independently.

Basic Syntax

SELECT column, aggregate_function(column)
FROM table
WHERE condition
GROUP BY column;

Example 1: Total Revenue by City

SELECT City, SUM(Amount) AS Total
FROM Orders
GROUP BY City;
CityTotal
Mumbai140000
Delhi65000
Bangalore25000

Example 2: Average Order Value by Customer

SELECT Customer, AVG(Amount) AS Avg_Order
FROM Orders
GROUP BY Customer;
CustomerAvg_Order
Rahul32500.00
Priya32500.00
Amit37500.00
Sneha25000.00

Example 3: Total Quantity Sold per Product

SELECT Product, SUM(Quantity) AS Total_Qty
FROM Orders
GROUP BY Product;
ProductTotal_Qty
Laptop3
Phone3
Tablet3

GROUP BY with Multiple Columns

You can group by multiple columns to get more granular summaries.

SELECT City, Product, SUM(Amount) AS Total
FROM Orders
GROUP BY City, Product;
CityProductTotal
MumbaiLaptop100000
MumbaiPhone15000
MumbaiTablet25000
DelhiLaptop50000
DelhiPhone15000
BangaloreTablet25000

This shows revenue for each product within each city.


Common GROUP BY Mistake

All columns in the SELECT clause must either be in GROUP BY or be wrapped in an aggregate function.

-- WRONG: Customer is not in GROUP BY and not aggregated
SELECT Customer, City, SUM(Amount)
FROM Orders
GROUP BY City;

-- RIGHT: Customer is in GROUP BY
SELECT Customer, City, SUM(Amount)
FROM Orders
GROUP BY Customer, City;

-- ALSO RIGHT: Customer is aggregated
SELECT City, COUNT(DISTINCT Customer) AS Customer_Count
FROM Orders
GROUP BY City;

HAVING

HAVING filters groups after aggregation.

WHERE filters rows before grouping. HAVING filters groups after grouping.

Example

Find cities with total revenue greater than 50,000.

SELECT City, SUM(Amount) AS Total
FROM Orders
GROUP BY City
HAVING SUM(Amount) > 50000;
CityTotal
Mumbai140000
Delhi65000

WHERE + GROUP BY + HAVING

Find cities where Laptop sales exceed 40,000.

SELECT City, SUM(Amount) AS Laptop_Sales
FROM Orders
WHERE Product = 'Laptop'
GROUP BY City
HAVING SUM(Amount) > 40000;

Execution order: WHERE (filter to laptops) → GROUP BY city → HAVING (keep cities with > 40000 laptop sales).


WHERE vs HAVING

AspectWHEREHAVING
When it appliesBefore GROUP BYAfter GROUP BY
Can use aggregatesNoYes
FiltersIndividual rowsGroups
ExampleWHERE Amount > 10000HAVING SUM(Amount) > 50000

DISTINCT with Aggregates

DISTINCT inside an aggregate counts only unique values.

-- Total customers
SELECT COUNT(DISTINCT Customer) FROM Orders;  -- 4

-- Cities with at least one order
SELECT COUNT(DISTINCT City) FROM Orders;  -- 3

Without DISTINCT:

SELECT COUNT(Customer) FROM Orders;  -- 7 (counting every row)

NULL Behavior in Aggregates

FunctionNULL Behavior
COUNT(*)Counts all rows, including NULLs
COUNT(column)Ignores NULLs in that column
SUM(column)Ignores NULLs (treats as 0)
AVG(column)Ignores NULLs in the average
MAX/MIN(column)Ignores NULLs
-- If Amount has a NULL value:
SELECT AVG(Amount) FROM Orders;  -- Average of non-NULL amounts only
SELECT COUNT(*) FROM Orders;     -- Total rows including NULL
SELECT COUNT(Amount) FROM Orders;  -- Non-NULL amounts only

Advanced Grouping Features

These features allow multi-dimensional aggregation in a single query.

GROUPING SETS

Allows you to specify multiple groupings in one query.

SELECT City, Product, SUM(Amount) AS Total
FROM Orders
GROUP BY GROUPING SETS (
    (City, Product),  -- detailed level
    (City),           -- subtotal by city
    (Product),        -- subtotal by product
    ()                -- grand total
);

ROLLUP

Generates hierarchical subtotals from most detailed to grand total.

SELECT City, Product, SUM(Amount) AS Total
FROM Orders
GROUP BY ROLLUP (City, Product);

This generates:

  • Total for (Mumbai, Laptop), (Mumbai, Phone), etc.
  • Subtotal for Mumbai.
  • Subtotal for Delhi.
  • Grand total.

Useful for reporting: Year → Quarter → Month → Total.

CUBE

Generates all possible combinations of groupings.

SELECT City, Product, SUM(Amount) AS Total
FROM Orders
GROUP BY CUBE (City, Product);

This includes every combination: (City, Product), (City), (Product), ().

CUBE generates 2^n grouping sets (where n is the number of columns). For 3 columns, that is 8 groupings.


Aggregation Best Practices

PracticeWhy
Always use table aliasesOrders o instead of Orders
Always use column aliasesSUM(Amount) AS Total
Use COUNT(*) for row countsFastest, includes NULLs
Use COUNT(DISTINCT) for unique countsCorrect for unique value analysis
Remember NULLsAggregates ignore NULLs unless using COUNT(*)
Test HAVING without it firstRun the GROUP BY query, then add HAVING

Interview Deep Dive

Q: Can you use HAVING without GROUP BY?

A: Yes, but it behaves like WHERE when no grouping is involved. However, the standard practice is to use HAVING only with GROUP BY. Using HAVING without GROUP BY is confusing and can be rewritten as WHERE.

Q: What is the difference between COUNT(*) and COUNT(column)?

A: COUNT() counts all rows including those with NULL values. COUNT(column) counts only non-NULL values in the specified column. They can return different results if the column has NULLs. COUNT() is also faster because it doesn’t check for NULLs.

Q: When would you use ROLLUP in a real report?

A: A sales dashboard showing revenue by Year, Quarter, and Month. ROLLUP generates the sub-totals for each year and a grand total in a single query. Without ROLLUP, you would need multiple queries (one for each level) and combine them in application code.

Q: What does AVG return if all values are NULL?

A: AVG returns NULL if all values are NULL (or if the table is empty). This can cause unexpected behavior in applications. You can handle this with COALESCE: SELECT COALESCE(AVG(Amount), 0) FROM Orders;


Key Takeaways

  • Five aggregate functions: COUNT, SUM, AVG, MAX, MIN.
  • GROUP BY creates groups; aggregates apply per group.
  • WHERE filters before grouping; HAVING filters after.
  • All non-aggregated columns in SELECT must be in GROUP BY.
  • COUNT(*) includes NULLs; COUNT(column) excludes them.
  • DISTINCT inside aggregates counts unique values.
  • GROUPING SETS, ROLLUP, and CUBE enable multi-dimensional aggregation.
  • NULLs are ignored by most aggregates except COUNT(*).

My Private Notes

Notes are auto-saved locally to this device.