Running total without window functions in SQL - Time & Space Complexity
We want to understand how the time needed to calculate a running total grows as the data size increases.
How does the number of operations change when we sum values step-by-step without special functions?
Analyze the time complexity of the following code snippet.
SELECT a.id, a.value,
(SELECT SUM(b.value) FROM table1 b WHERE b.id <= a.id) AS running_total
FROM table1 a
ORDER BY a.id;
This code calculates a running total by summing all values up to the current row's id without using window functions.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: For each row, the inner query sums values from the start up to that row.
- How many times: The inner sum runs once for every row in the table.
As the number of rows grows, the amount of work grows much faster because each new row sums more values.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 55 sums (1+2+...+10) |
| 100 | About 5,050 sums |
| 1000 | About 500,500 sums |
Pattern observation: The total work grows roughly like the square of the number of rows.
Time Complexity: O(n²)
This means if you double the number of rows, the work needed to calculate running totals roughly quadruples.
[X] Wrong: "Calculating running totals like this only takes time proportional to the number of rows."
[OK] Correct: Because for each row, the query sums all previous rows, so the total work adds up much faster than just one pass.
Understanding how nested queries affect performance helps you write better SQL and explain your reasoning clearly in interviews.
"What if we replaced the subquery with a window function like SUM() OVER()? How would the time complexity change?"
Practice
Solution
Step 1: Understand running total concept
A running total means adding values step-by-step, including all previous rows up to the current one.Step 2: Identify method without window functions
Without window functions, a common way is to use a subquery that sums all rows with an ID less than or equal to the current row's ID.Final Answer:
Use a subquery to sum all previous rows up to the current row. -> Option DQuick Check:
Running total = cumulative sum using subquery [OK]
- Using SUM() without filtering rows
- Trying to use GROUP BY for running total
- Confusing JOIN with running total calculation
amount column without using window functions, assuming id orders the rows?Solution
Step 1: Identify correct syntax without window functions
SELECT id, amount, (SELECT SUM(amount) FROM sales WHERE id <= s.id) AS running_total FROM sales s ORDER BY id; uses a correlated subquery summing amounts where id is less or equal to current row's id, which is correct for running total without window functions.Step 2: Check other options for errors
SELECT id, amount, SUM(amount) OVER (ORDER BY id) AS running_total FROM sales ORDER BY id; uses window function SUM() OVER, which is not allowed here. SELECT id, amount, SUM(amount) FROM sales GROUP BY id ORDER BY id; misuses GROUP BY without running total logic. SELECT id, amount, amount + LAG(amount) FROM sales ORDER BY id; uses LAG() which is a window function.Final Answer:
SELECT id, amount, (SELECT SUM(amount) FROM sales WHERE id <= s.id) AS running_total FROM sales s ORDER BY id; -> Option AQuick Check:
Subquery sums previous rows correctly [OK]
- Using window functions despite instructions
- Misusing GROUP BY without aggregation logic
- Using LAG() which is a window function
sales with rows:id | amount
1 | 10
2 | 20
3 | 15What is the output of this query?
SELECT id, amount, (SELECT SUM(amount) FROM sales WHERE id <= s.id) AS running_total FROM sales s ORDER BY id;
Solution
Step 1: Calculate running total for each row
For id=1: sum amounts where id <=1 is 10.
For id=2: sum amounts where id <=2 is 10+20=30.
For id=3: sum amounts where id <=3 is 10+20+15=45.Step 2: Match calculated values with options
1 | 10 | 10
2 | 20 | 30
3 | 15 | 45 matches these running totals exactly.Final Answer:
1 | 10 | 10
2 | 20 | 30
3 | 15 | 45 -> Option BQuick Check:
Running total sums all previous rows [OK]
- Summing only current row amount
- Not including all previous rows in sum
- Mixing up running total values
SELECT id, amount, (SELECT SUM(amount) FROM sales WHERE id < s.id) AS running_total FROM sales s ORDER BY id;
What is the main issue with this query?
Solution
Step 1: Analyze the subquery condition
The subquery sums amounts where id is less than current row's id, so it excludes the current row's amount.Step 2: Understand running total definition
Running total should include the current row's amount, so condition should be id <= s.id, not id < s.id.Final Answer:
It excludes the current row's amount from the running total. -> Option CQuick Check:
Use id <= current id to include current row [OK]
- Using < instead of <= in subquery condition
- Expecting window functions in this query
- Assuming syntax error due to GROUP BY
transactions table with columns trans_id, user_id, and amount. You want to calculate a running total of amount for each user_id ordered by trans_id, but you cannot use window functions. Which query correctly achieves this?Solution
Step 1: Understand requirement for per-user running total
Running total must reset for each user_id and sum amounts up to current trans_id.Step 2: Analyze options for correct filtering
SELECT t1.trans_id, t1.user_id, t1.amount, (SELECT SUM(t2.amount) FROM transactions t2 WHERE t2.user_id = t1.user_id AND t2.trans_id <= t1.trans_id) AS running_total FROM transactions t1 ORDER BY t1.user_id, t1.trans_id; uses a correlated subquery filtering by same user_id and trans_id less or equal current, correctly calculating running total per user.Step 3: Identify errors in other options
SELECT trans_id, user_id, amount, SUM(amount) OVER (PARTITION BY user_id ORDER BY trans_id) AS running_total FROM transactions ORDER BY user_id, trans_id; uses window functions (not allowed). SELECT trans_id, user_id, amount, SUM(amount) FROM transactions GROUP BY user_id, trans_id ORDER BY user_id, trans_id; misuses GROUP BY without running total logic. SELECT t1.trans_id, t1.user_id, t1.amount, (SELECT SUM(amount) FROM transactions WHERE trans_id <= t1.trans_id) AS running_total FROM transactions t1 ORDER BY user_id, trans_id; sums amounts ignoring user_id, mixing all users.Final Answer:
SELECT t1.trans_id, t1.user_id, t1.amount, (SELECT SUM(t2.amount) FROM transactions t2 WHERE t2.user_id = t1.user_id AND t2.trans_id <= t1.trans_id) AS running_total FROM transactions t1 ORDER BY t1.user_id, t1.trans_id; -> Option AQuick Check:
Subquery filters by user and trans_id for running total [OK]
- Ignoring user_id in running total calculation
- Using window functions despite restriction
- Not ordering results properly
Ready to prove it?
Pass the test and this pattern moves unknown
