Skip to content
article

the AI wrote me a running total that was 'almost right'. almost right is the worst kind

Mo Siddiqui@mo_boston

September 13, 2026 · 2 min read

ok quick one because this bit me today and i want to save someone an afternoon.

asked an ai assistant for a running total of revenue. it gave me this, which looks totally fine:

SELECT
  order_date,
  amount,
  SUM(amount) OVER (ORDER BY order_date) AS running_total
FROM orders;

ran it, numbers looked plausible, almost shipped it to the dashboard. then i noticed the running total jumped by the same amount on every row with the same date. if there were 3 orders on march 4th, all 3 rows already showed the total for the whole day.

here's why. when you put ORDER BY in a window and no frame clause, the default frame is:

RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW

RANGE means "every row whose order value is up to and including mine", so rows that tie on order_date (peers) all get included together. if you want a row by row running total you need ROWS, plus a tie breaker so the order is deterministic:

SELECT
  order_date,
  order_id,
  amount,
  SUM(amount) OVER (
    ORDER BY order_date, order_id
    ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
  ) AS running_total
FROM orders;

the default is the same in postgres, sql server and bigquery (check yours). if you actually want one running total per day, aggregate first and then window, which is clearer anyway:

SELECT
  order_date,
  daily,
  SUM(daily) OVER (ORDER BY order_date) AS running_total
FROM (
  SELECT order_date, SUM(amount) AS daily
  FROM orders
  GROUP BY order_date
) d;

(no ties after grouping, so RANGE vs ROWS doesn't matter there.)

the ai wasn't wrong exactly. it answered a slightly different question than the one i asked and the output looked right. that scares me more than obviously broken code. obviously broken code at least fails loudly lol

Written by

Mo Siddiqui

data analyst at a biotech in cambridge MA. SQL, dashboards, celtics. here for the tech, not the politics.

Get the best of #my-model-is-fine weekly

One email each Monday with the most useful posts. No spam; unsubscribe in one click.

Comments

7

Sign in to join the conversation.

Sign in to comment
Nurul Aisyah@nurul_sql#2 in My Model Is Fine, The Data Is Not
alamak this one got me last year also. finance said the running total "jumps funny" on month end. confirm RANGE. now I always write ROWS explicitly even when not needed, so the next person don't need to think
1 net votes
Lin Wei@linwei#1 in My Model Is Fine, The Data Is Not
Good write-up. The tie breaker is the subtle part. Even with ROWS, ORDER BY order_date alone gives non-deterministic results across runs when there are ties, and then someone opens a ticket because the dashboard changed overnight.
2 net votes
Mo Siddiqui@mo_boston#4 in My Model Is Fine, The Data Is Not
yep. we literally had that ticket last year. 'nobody touched it and the numbers moved' 🙄
1 net votes
Dana Kowalski@dana_ml#3 in My Model Is Fine, The Data Is Not
'Answered a slightly different question and the output looked right' is the most precise description of the failure mode I know. Same thing happens with generated pandas: groupby then cumsum versus cumsum then groupby.
0 net votes
Ryan Goh@ai_maxi_ryan1 rep in My Model Is Fine, The Data Is Not
should have asked it to double check its own query bro, thats prompting 101
1 net votes
Mo Siddiqui@mo_boston#4 in My Model Is Fine, The Data Is Not
i did. it said the query was correct. it WAS correct, for RANGE. that's the whole post man
2 net votes
Danny Cho@danny_cho_la#10 in My Model Is Fine, The Data Is Not
Saving this for interviews. 'What is the default frame when you add ORDER BY to a window function?' separates the people who have been burned from the people who haven't.
2 net votes