Add ORDER BY and Your SUM Means Something Completely Different

Advanced 5 min read Updated 30 Jul 2026 UPA AI Partner

Same SUM function. With ORDER BY it is a running total; without it, a group total. Nobody tells you this.

The problem

The chart needs a cumulative revenue line from the start of the month, plus a 3-day moving average to smooth it.

GOCngaydoanh_thu01100028003120SAU OVERngaydoanh_thuluy_ketb_3_ngay0110010010002801809003120300100SUM OVER (ORDER BY)

Running total

SELECT
  day,
  revenue,
  SUM(revenue) OVER (ORDER BY day) AS running_total
FROM daily_revenue;
The detail that changes everything SUM(x) OVER () gives the grand total.
SUM(x) OVER (ORDER BY day) gives the total up to the current row.
One clause apart, entirely different meanings.

Moving average

SELECT
  day,
  ROUND(AVG(revenue) OVER (
    ORDER BY day
    ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
  ), 1) AS avg_3_day
FROM daily_revenue;

ROWS BETWEEN defines the frame — how many rows are included:

Written asMeans
2 PRECEDING AND CURRENT ROWthe last 3 rows
UNBOUNDED PRECEDING AND CURRENT ROWstart to now (the default)
1 PRECEDING AND 1 FOLLOWINGbefore, current, after
CURRENT ROW AND UNBOUNDED FOLLOWINGnow to the end

Common mistakes

1. Confusing ROWS with RANGE

ROWS counts rows. RANGE counts by value, so every row sharing a date collapses into one step. With duplicate dates the two give different answers. The default is RANGE — the source of many baffling numbers.

2. Forgetting PARTITION BY

-- WRONG: the running total bleeds across cities
SUM(revenue) OVER (ORDER BY day)

-- RIGHT
SUM(revenue) OVER (PARTITION BY city ORDER BY day)

3. Missing days breaking the moving average

ROWS 2 PRECEDING takes 3 rows, not 3 days. If days are missing, the window slides across the gap unnoticed. Build a complete date range first.

A shortcut when you reuse the same frame

SELECT
  day,
  SUM(revenue) OVER w      AS running_total,
  AVG(revenue) OVER w      AS running_avg,
  MAX(revenue) OVER w      AS running_max
FROM daily_revenue
WINDOW w AS (ORDER BY day);

Practice

  • LeetCode 1321 — Restaurant Growth (7-day moving average)
  • LeetCode 579 — Find Cumulative Salary of an Employee
  • LeetCode 1204 — Last Person to Fit in the Bus
  • LeetCode 534 — Game Play Analysis III
  • HackerRank — Weather Observation Station 20 (median)

Key takeaways

  • ORDER BY inside OVER turns SUM into a running total.
  • ROWS BETWEEN defines the moving-average frame.
  • ROWS counts rows, RANGE counts values — RANGE is the default.
  • Multiple groups require PARTITION BY.
  • Use WINDOW w AS (…) when repeating the same frame.

You have just learned luỹ kế và trung bình trượt

Ready to practise?

Work through luỹ kế và trung bình trượt exercises on real data, graded the moment you hit run.

Start practising →