Intermediate
Aggregation
Summarize data using aggregate functions, GROUP BY for grouping, and HAVING for filtering aggregated results.
Aggregate Functions
Aggregate functions compute a single value from a set of rows:
SQL
SELECT COUNT(*) AS total_orders, COUNT(DISTINCT user_id) AS unique_customers, SUM(total) AS revenue, AVG(total) AS avg_order_value, MIN(total) AS smallest_order, MAX(total) AS largest_order FROM orders;
| Function | Description | NULL Handling |
|---|---|---|
COUNT(*) | Count all rows | Includes NULLs |
COUNT(col) | Count non-NULL values | Excludes NULLs |
SUM(col) | Sum of values | Ignores NULLs |
AVG(col) | Average of values | Ignores NULLs |
MIN(col) | Minimum value | Ignores NULLs |
MAX(col) | Maximum value | Ignores NULLs |
GROUP BY
GROUP BY groups rows that have the same values in specified columns, then applies aggregate functions to each group:
SQL
-- Revenue by category SELECT category, COUNT(*) AS num_products, AVG(price) AS avg_price, SUM(price * quantity) AS total_revenue FROM products GROUP BY category ORDER BY total_revenue DESC; -- Monthly order summary SELECT DATE_TRUNC('month', order_date) AS month, COUNT(*) AS orders, SUM(total) AS revenue FROM orders GROUP BY DATE_TRUNC('month', order_date) ORDER BY month;
Rule: Every column in your SELECT must either be in the GROUP BY clause or wrapped in an aggregate function. Mixing grouped and ungrouped columns is a common SQL error.
HAVING
HAVING filters groups after aggregation, just like WHERE filters rows before aggregation:
SQL
-- Categories with more than 10 products SELECT category, COUNT(*) AS num_products, AVG(price) AS avg_price FROM products GROUP BY category HAVING COUNT(*) > 10 ORDER BY num_products DESC; -- Customers who spent more than $1000 SELECT u.name, SUM(o.total) AS total_spent FROM users u INNER JOIN orders o ON u.id = o.user_id GROUP BY u.name HAVING SUM(o.total) > 1000;
WHERE vs HAVING
| Clause | Filters | When Applied | Can Use Aggregates? |
|---|---|---|---|
| WHERE | Individual rows | Before GROUP BY | No |
| HAVING | Groups | After GROUP BY | Yes |
SQL
-- Combine WHERE and HAVING SELECT category, COUNT(*) AS num_products, AVG(price) AS avg_price FROM products WHERE price > 10 -- filter rows first GROUP BY category HAVING COUNT(*) >= 5 -- then filter groups ORDER BY avg_price DESC;
SQL Query Execution Order
SQL executes in this order: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT. Understanding this order helps you write correct queries and debug errors.
Lilly Tech Systems