N057-H2 Tier 4 · Advanced · hard ecommerce · Brightlane

Return each calendar day in January 2024 on which at least one order was placed, the number of `orders` placed on that day, and the total `orders` revenue

Part of Grouping by Date Periods in SQL

The problem

Scenario: Brightlane's operations team is preparing a January 2024 month-end reconciliation and needs a daily breakdown of order activity for that month only.

Task: Write a query to return each calendar day in January 2024 on which at least one order was placed, the number of orders placed on that day, and the total orders revenue.

Assumptions:

  • The orders table holds one row per placed order, with the placement timestamp stored in ordered_at and the order amount stored in total_amount.
  • A calendar day is identified by its date and covers every order placed within that day.
  • The result covers only orders placed within January 2024 — that is, on or after January 1, 2024 and on or before January 31, 2024. Days within the window with no recorded orders do not appear in the result.

Output:

  • One row per calendar day in January 2024 with at least one placed order.
  • Columns in this order: day, order_count, daily_revenue.
Schema · ecommerce 5 tables
categories
id integer
name text
parent_id? integer
products
id integer
name text
category_id integer
price numeric
stock_qty integer
attributes? jsonb
order_items
id integer
order_id integer
product_id integer
quantity integer
unit_price numeric
customers
id integer
name text
email text
city? text
country text
created_at timestamptz
is_active boolean
orders
id integer
customer_id integer
ordered_at timestamptz
status text
total_amount numeric

Run previews · Check grades

Write a query, then run it to see results here.

Worked solution Try it yourself first
Solution query
SELECT
  DATE_TRUNC('day', ordered_at)::date AS DAY,
  COUNT(*) AS order_count,
  SUM(total_amount) AS daily_revenue
FROM
  orders
WHERE
  DATE_TRUNC('month', ordered_at)::date = '2024-01-01'::date
GROUP BY
  DATE_TRUNC('day', ordered_at)

The shape

The WHERE clamps the data to a single calendar month before any grouping runs, then date_trunc('day', ordered_at) collapses every surviving timestamp to its calendar day. The aggregate returns one row per day inside January 2024 that had at least one order, with the count and the day's revenue.

Clause by clause

  • SELECT date_trunc('day', ordered_at)::date AS day, COUNT(*) AS order_count, SUM(total_amount) AS daily_revenue returns one row per day with three columns. The ::date cast normalises the truncated timestamp to a plain date. COUNT(*) counts the day's orders and SUM(total_amount) adds up the dollar amounts.
  • FROM orders reads every placed order before the filter runs.
  • WHERE date_trunc('month', ordered_at)::date = '2024-01-01'::date keeps only orders whose month-truncated date equals January 1, 2024. Every order placed at any time during January 2024 truncates to that same value, so the filter selects exactly the January 2024 rows and discards everything else before grouping starts.
  • GROUP BY date_trunc('day', ordered_at) repeats the day-truncation as the grouping key. Two orders placed at different times on January 5, 2024 both truncate to 2024-01-05 and land in the same day's row.

Why filter before grouping and not after

The WHERE runs before GROUP BY in SQL's evaluation order, which is what makes this filter cheap and correct. Non-January rows are discarded before the aggregator ever sees them, so no group is ever built for a February or March day, and the result naturally contains only days in the window. A filter applied after grouping would have to run against an aggregated result and is a different construct entirely. Here the question is "which raw rows belong in scope," and that question is exactly what WHERE answers.

The trap

Days inside January 2024 with no recorded orders do not appear in the result. GROUP BY only produces a row for a value that actually exists in the underlying rows, so January 5 and January 12 appear because they have orders, but January 6 and January 11 are absent because nothing was placed on those days. The output of six rows for January 2024 is not a one-row-per-day-of-the-month report; it is a one-row-per-day-with-activity report. A full calendar view would need a separately-generated date spine joined to this result, which is a different problem.

You practiced restricting orders to a single calendar month before truncating to day, so the report only contains days within the targeted reconciliation window.

How you actually get good at SQL

Reading explains SQL. Writing it, over and over with instant feedback, is what makes you fluent.

That's the whole SQLMaxx loop: 600+ real problems, instant AI feedback, mastery you can actually see, and spaced review that won't let you forget.

A stack of SQL practice problem cards, the top card showing an employees table.
615 problems · 66 concepts

Real problems. Not toy examples.

615 hand-built problems spanning all 66 concepts, from basic SELECTs to window functions, built on real schemas and real business questions, the kind you'll actually get asked on the job. Enough reps to make SQL automatic.

A retro computer showing a SQL query marked correct with a green checkmark.
Instant AI feedback

Write a query. Know if it's right in one second.

No copying an answer and hoping it clicked. The AI grader checks your real query against real data, catches exactly what's wrong, and explains the fix in plain English, like a senior analyst reading over your shoulder on every problem.

A circular mastery progress dial filling from blue to green, the SQLMaxx diamond at its center.
Mastery tracking

Stop guessing whether you actually know it.

SQLMaxx tracks every concept and shows you what you've mastered and what's still shaky. Your skills fill in one concept at a time, so 'I think I get joins' becomes something you can prove.

A SQL query editor circled by a blue return arrow with a clock, scheduled to come back for review.
Spaced review

Learn it once. Keep it for good.

Most of what you learn this week fades by next week. So when a concept comes due for review, SQLMaxx hands you a fresh problem to solve from a blank editor, not a flashcard to re-read. A research-backed spaced-repetition algorithm (FSRS) times each return for right before you'd forget, so your SQL is still there months later, when the interview or the job actually needs it.

Practice, feedback, mastery, review. That's the loop that turns reading into real skill.

Start free

No account, no credit card. Start solving in under a minute.