Back to Blog

Why Your AI's 'Daily Revenue' Is One Row Per Second

Asked for revenue by day and got hundreds of rows, each with a tiny total. GROUP BY created_at groups by the exact timestamp, not the calendar day. Two orders one second apart are two 'days.'

You ask for "revenue by day this month." You get 400 rows. Each "day" is a few dollars. The month has 30 days. GROUP BY created_at kept the time. Every distinct second is its own bucket.

A timestamp is not a day

-- one group per exact instant
SELECT created_at, SUM(amount)
FROM orders
GROUP BY created_at

-- one group per calendar day
SELECT DATE_TRUNC('day', created_at) AS day, SUM(amount)
FROM orders
GROUP BY 1

Two orders at 10:00:01 and 10:00:02 are two groups. The SQL runs. A spreadsheet or a chart treats each row as a day, so "our best day" is whichever second happened to hold the biggest single charge.

This is not HAVING vs WHERE (the threshold never runs) and not latest row per group (the other columns came from the wrong sibling). The grain of the GROUP BY is just finer than the English.

GROUP BY DATE(created_at) in MySQL and CAST(created_at AS date) in Postgres are the same fix. GROUP BY created_at::date still needs the timezone you mean if the column is timestamptz — otherwise the "day" boundary is UTC midnight. Pair that with the timezone note.

How to catch it

COUNT(*) of the grouped result
vs COUNT(DISTINCT created_at::date) over the same filter
If the first is much larger, you grouped by the timestamp.
Also: SUM of the group totals must equal SUM(amount) with no GROUP BY.

If the row count is close to the number of orders, nothing was aggregated by day at all.

The prompt that heads this off

When I say by day / by week / by month, group by a truncated timestamp
(DATE_TRUNC or a date cast), not by the raw created_at column.
Show the number of groups. A month of days should be about 28–31 rows,
not hundreds.

The short version

GROUP BY created_at means "by this exact time." "By day" means one bucket per date. If daily revenue has a row for every order, the time never got truncated.


Read-only MCP gateway: mcpserver.design.

Related reading: