SQL for Data Analysis

Aggregate Functions and GROUP BY

Lesson 4 of 13

Analysis is mostly summarising: how many, how much, on average. Aggregate functions turn many rows into one value.

The five core functions

SELECT COUNT(*)   AS products,
       MIN(price) AS cheapest,
       MAX(price) AS dearest,
       AVG(price) AS average_price,
       SUM(price) AS total_of_prices
FROM products;

COUNT and NULL

COUNT(*) counts rows. COUNT(column) counts rows where that column is not NULL. COUNT(DISTINCT column) counts different values.

SELECT COUNT(*)             AS customers,
       COUNT(city)          AS with_city,
       COUNT(DISTINCT city) AS different_cities
FROM customers;

All aggregate functions except COUNT(*) ignore NULL.

GROUP BY

GROUP BY splits the rows into groups and calculates the aggregate for each one.

SELECT category,
       COUNT(*)   AS products,
       AVG(price) AS average_price
FROM products
GROUP BY category;

The rule: every column in the SELECT list must either be in the GROUP BY clause or be inside an aggregate function. Most databases reject a query that breaks this rule. SQLite allows it and returns a value from an arbitrary row, which is rarely what you want.

Grouping by more than one column

SELECT customer_id,
       status,
       COUNT(*) AS orders
FROM orders
GROUP BY customer_id, status
ORDER BY customer_id;

Filtering groups with HAVING

WHERE filters rows before they are grouped. HAVING filters the groups afterwards, so it can use aggregates.

SELECT customer_id,
       COUNT(*) AS orders
FROM orders
GROUP BY customer_id
HAVING COUNT(*) >= 2;

Use both together when you need to:

SELECT customer_id,
       COUNT(*) AS delivered_orders
FROM orders
WHERE status = 'delivered'
GROUP BY customer_id
HAVING COUNT(*) >= 2
ORDER BY delivered_orders DESC;

Rounding

SELECT category,
       ROUND(AVG(price), 2) AS average_price
FROM products
GROUP BY category;

The order a query is processed in

You write a query in one order, but the database works through it in another:

1. FROM      pick the table
2. WHERE     filter rows
3. GROUP BY  form groups
4. HAVING    filter groups
5. SELECT    calculate the output columns
6. ORDER BY  sort
7. LIMIT     cut

This explains why WHERE cannot use an aggregate (the groups do not exist yet) and why ORDER BY can use an alias from SELECT.

Practice

For each order, show the number of different products and the total quantity of items, using the order_items table.