SQL - MySQL

Module 01 - Database Theory
1. Introduction to Databases2. DBMS Theory Concepts3. Types of Keys4. Database Relationships5. DBMS Interview Questions
Module 02 - CRUD Operations
1. Create - INSERT2. Read - SELECT3. Update - UPDATE4. Delete - DELETE5. Alter - ALTER TABLE
Module 03 - Querying
1. Joins2. Filtering and sorting3. Practice - Filtering and Sort...4. Aggregate functions5. Practice - Aggregate Function...
Module 04 - Data Integrity
1. Constraints in Depth2. Transactions
MySQL Playground
ProfileProfile
Akkal DhamiFull Stack Developer

Building modern web experiences with a focus on performance, scalability, and clean architecture.

© 2026 | Akkal Dhami | All rights reserved

Built with
byAkkal Dhami

Navigation

  • Projects
  • Dev Setup
  • Playbook
  • Templates
  • Networking
  • SQL - MySQL
  • SQL Playground
  • System Design
  • DSA
AKKAL DHAMIAKKAL DHAMIAKKAL DHAMI

Aggregate Functions

Aggregate functions take many rows and collapse them into a single summary value — total, average, count, min, max. Instead of looking at each row individually, you're asking a question about the whole set.

We'll keep using the books table:

create-table.sql
CREATE TABLE books (
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    author VARCHAR(255),
    genre VARCHAR(100),
    price DECIMAL(6,2),
    published_year INT,
    in_stock BOOLEAN DEFAULT TRUE
);
insert-value.sql
INSERT INTO books (title, author, genre, price, published_year, in_stock) VALUES
('Dune', 'Frank Herbert', 'Sci-Fi', 15.50, 1965, TRUE),
('1984', 'George Orwell', 'Dystopian', 9.99, 1949, TRUE),
('Animal Farm', 'George Orwell', 'Satire', 7.50, 1945, FALSE),
('Brave New World', 'Aldous Huxley', 'Dystopian', 10.25, 1932, TRUE),
('Foundation', 'Isaac Asimov', 'Sci-Fi', 12.00, 1951, FALSE);

mysql> SELECT * FROM books; +----+-----------------+---------------+-----------+-------+----------------+----------+ | id | title | author | genre | price | published_year | in_stock | +----+-----------------+---------------+-----------+-------+----------------+----------+ | 1 | Dune | Frank Herbert | Sci-Fi | 15.50 | 1965 | 1 | | 2 | 1984 | George Orwell | Dystopian | 9.99 | 1949 | 1 | | 3 | Animal Farm | George Orwell | Satire | 7.50 | 1945 | 0 | | 4 | Brave New World | Aldous Huxley | Dystopian | 10.25 | 1932 | 1 | | 5 | Foundation | Isaac Asimov | Sci-Fi | 12.00 | 1951 | 0 | +----+-----------------+---------------+-----------+-------+----------------+----------+


1. The core functions

example.sql
SELECT COUNT(*) FROM books;          -- how many rows: 5
SELECT SUM(price) FROM books;        -- total of all prices
SELECT AVG(price) FROM books;        -- average price
SELECT MIN(price) FROM books;        -- cheapest price
SELECT MAX(price) FROM books;        -- most expensive price

Each of these returns one row, not one per book — they summarize the whole table.


2. GROUP BY — the real power move

GROUP BY splits rows into buckets first, then runs the aggregate on each bucket separately.

Average price per genre:

example.sql
SELECT genre, AVG(price) AS avg_price
FROM books
GROUP BY genre;

Result: one row per genre, each with its own average — not one average for everything.

+-----------+-----------+ | genre | avg_price | +-----------+-----------+ | Sci-Fi | 13.750000 | | Dystopian | 10.120000 | | Satire | 7.500000 | +-----------+-----------+

How many books per author:

example.sql
SELECT author, COUNT(*) AS book_count
FROM books
GROUP BY author;

+---------------+------------+ | author | book_count | +---------------+------------+ | Frank Herbert | 1 | | George Orwell | 2 | | Aldous Huxley | 1 | | Isaac Asimov | 1 | +---------------+------------+

Key rule: every column in SELECT must either be inside an aggregate function, or listed in GROUP BY. This fails:

example.sql
SELECT genre, price, AVG(price) FROM books GROUP BY genre;
-- ERROR — price isn't aggregated or grouped

3. HAVING — filtering after grouping

WHERE filters rows before grouping. HAVING filters the groups themselves, after aggregation.

Genres with more than 1 book:

example.sql
SELECT genre, COUNT(*) AS book_count
FROM books
GROUP BY genre
HAVING COUNT(*) > 1;

+-----------+------------+ | genre | book_count | +-----------+------------+ | Sci-Fi | 2 | | Dystopian | 2 | +-----------+------------+

You can't write WHERE COUNT(*) >= 1 — COUNT(*) doesn't exist yet at the point WHERE runs.


4. Combining WHERE and HAVING

Among in-stock books, show genres averaging over $10:

example.sql
SELECT genre, AVG(price) AS avg_price
FROM books
WHERE in_stock = TRUE
GROUP BY genre
HAVING AVG(price) > 10;

+-----------+-----------+ | genre | avg_price | +-----------+-----------+ | Sci-Fi | 15.500000 | | Dystopian | 10.120000 | +-----------+-----------+

Order matters: WHERE filters rows first → then GROUP BY buckets them → then HAVING filters the buckets → then results are returned.


5. Quick Reference

ClauseRuns whenFilters
WHEREBefore groupingIndividual rows
GROUP BY—Buckets rows into groups
HAVINGAfter groupingThe groups themselves

Practice - Filtering and Sorting
Practice - Aggregate Functions