Scenario: Brightlane's CFO wants a quarterly revenue report where year and quarter are reported as two separate numeric columns rather than a single date, so the values are unambiguous on the report.
Task: Write a query to return the year, the quarter number from 1 through 4, the number of orders placed in that year-quarter, and the total orders revenue for that year-quarter.
Assumptions:
- The
yearvalue is the four-digit calendar year of the order. - The
quartervalue is the calendar quarter of the order, expressed as a number from 1 (January–March) through 4 (October–December). - One result row covers every order whose calendar year and calendar quarter match.
Output:
- One row per (
year,quarter) combination present in the data. - Columns in this order:
year,quarter,order_count,revenue.
Schema · ecommerce 5 tables
Run previews · Check grades
Write a query, then run it to see results here.
Worked solution Try it yourself first
SELECT
EXTRACT(
YEAR
FROM
ordered_at
) AS YEAR,
EXTRACT(
quarter
FROM
ordered_at
) AS quarter,
COUNT(*) AS order_count,
SUM(total_amount) AS revenue
FROM
orders
GROUP BY
EXTRACT(
YEAR
FROM
ordered_at
),
EXTRACT(
quarter
FROM
ordered_at
) The shape
EXTRACT pulls the year and the quarter number out of ordered_at as two independent numeric values, and the grouping happens on the pair. Each row reports the time dimension as two unambiguous columns of numbers, not as a single truncated quarter-start date, which is the shape the CFO's report needs.
Clause by clause
SELECT EXTRACT(year FROM ordered_at) AS year, EXTRACT(quarter FROM ordered_at) AS quarter, COUNT(*) AS order_count, SUM(total_amount) AS revenuereturns one row per (year, quarter) combination. EachEXTRACTreturns a number, so the output shows2024and3instead of a July 1 date.COUNT(*)andSUM(total_amount)aggregate the orders that fall inside the pair.FROM ordersreads every placed order.GROUP BY EXTRACT(year FROM ordered_at), EXTRACT(quarter FROM ordered_at)uses the same two expressions as the grouping keys. The grouping is on the pair: Q1 2024 and Q1 2025 are kept apart by theyearvalue even though they sharequarter = 1.
Why EXTRACT for both columns
date_trunc('quarter', ordered_at) would return one timestamp per quarter — for example, 2024-07-01 for Q3 2024 — which is correct for charting but ambiguous on a printed report. A reader sees July 1 and has to mentally translate it back to "Q3." Pulling the year and the quarter number out as two separate columns removes that translation step. The two functions both live in this node for exactly this reason: date_trunc keeps the calendar continuous, and EXTRACT decomposes it into parts that a human can read at a glance.
The trap
The year and quarter columns are both numeric, and a reader who tries to combine them as if they were a single date — for example, by computing year * 10 + quarter to sort the result — will get a number that sorts correctly but means nothing as a date. The two columns are independent calendar parts. They sort lexicographically in pairs: (2022, 1), (2022, 2), ... (2022, 4), (2023, 1), ... which is the natural calendar order, but only if both columns are sorted together. Treating either as a standalone scalar that encodes both dimensions throws away information the report deliberately separated.
You practiced extracting year and quarter as independent numeric values so the time dimension is reported as two columns rather than a single truncated date.