IntermediateSQL · 25 min

Running Totals: Intermediate

Running totals show up in every business question that involves cumulative behavior. Year-to-date revenue. Lifetime value. Trailing 7-day active users. Balance at end of day. The query itself is small. The interview signal is whether you reach for SUM OVER without thinking, whether you state the right frame clause, and whether you recognize the variants the interviewer will hand you next. Most candidates can write a running total. Few candidates can defend the frame clause when the interviewer points at it.
list
Recognize cumulative-behavior questions on first read and reach for SUM() OVER
chart
Write the canonical running total with the right partition, order, and frame clause
branch
Articulate the silent ROWS-vs-RANGE bug that costs candidates the offer
code
Extend the pattern to rolling windows, period resets, and conditional cumulative sums

SUM OVER with the Right Frame

Daily Life
Interviews

Recognize that any question asking for cumulative, year-to-date, or rolling aggregates is a running total pattern.

Here is the most common shape this question takes. The interviewer hands you a transactions table and says 'compute the running balance per account over time.' Two minutes later they have a sense of where you sit on the depth axis. Not from your syntax, but from how cleanly you say what you are doing. The answer that lands names the pattern, names the frame clause, and writes the query in twelve lines. The answer that flatlines writes the same twelve lines but cannot explain why the frame clause matters when the interviewer points at it. The interviewer can tell the difference in seconds.
You are being tested on running totals when you hear:
  • "compute the cumulative ... over time"
  • "running balance," "running total," "running count"
  • "year-to-date," "life-to-date," "month-to-date"
  • "7-day rolling ..." or "30-day trailing ..."
  • "What was the value as of date X?"

What the interviewer is actually testing

They already know you can write SUM OVER. The signal they care about is whether you understand the underlying mechanic: a window function is a partial aggregation across an ordered window of rows that travels with each output row. If you understand that sentence, you can build any cumulative or rolling query from first principles. If you do not, you will get the basic running total right and then stall on the first follow-up. This lesson is about closing that gap.

Pause before writing the query and ask one question: 'is this per account, or across all accounts?' That single clarifier signals that you treat the partition clause as a business decision, not a default. Most candidates assume per-account. Naming it out loud is the move of someone who has been burned by aggregating across the wrong grain.

The frame everyone should know by heart

SUM(amount) OVER (PARTITION BY account_id ORDER BY txn_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW). The PARTITION BY isolates each account. The ORDER BY defines the row ordering. The ROWS BETWEEN clause defines the window: every row from the start of the partition up to and including the current row. This is the canonical running total. Memorize the shape. Type it without thinking. The interview signal is your speed, not your novelty.
Weak opening
  • "I'll use SUM with a GROUP BY."
  • Writes a per-date total, not a running total
  • Has to be corrected by the interviewer
  • Spends time recovering instead of going deep
Strong opening
  • "This is a running total. I'll use SUM OVER, partitioned by account, ordered by date."
  • States the frame clause explicitly: UNBOUNDED PRECEDING to CURRENT ROW
  • Mentions ROWS vs RANGE before being asked
  • Sets up the follow-up about the bug RANGE introduces

ROWS vs RANGE: The Silent Bug

Daily Life
Interviews

Write a correct running total using SUM() OVER (ORDER BY ... ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW).

The canonical running total query is the one you should be able to write while talking. PARTITION BY the grouping column, ORDER BY the time column, frame clause UNBOUNDED PRECEDING to CURRENT ROW. The frame clause is what makes it a running total rather than a partition-wide aggregate.
/* Running balance per account, ordered by transaction date */
SELECT
account_id,
txn_date,
amount,
SUM(amount) OVER (
PARTITION BY account_id
ORDER BY txn_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS running_balance
FROM transactions
ORDER BY account_id, txn_date

Why each clause matters

PARTITION BY account_id: the running total resets at every new account. Without it, the SUM runs across every transaction in the table, regardless of which account it belongs to. ORDER BY txn_date: defines the temporal order within each partition. Without ORDER BY, the running total is undefined (the engine can return rows in any order). ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW: the frame. UNBOUNDED PRECEDING means 'from the very first row in the partition.' CURRENT ROW means 'up to and including this row.' Together they make the window grow by one row at each output.

What happens without the frame clause

If you omit the frame clause entirely, most engines default to RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW. RANGE looks similar to ROWS but treats ties in the ORDER BY column as a single peer group. If two transactions land on the same txn_date, they share one cumulative value, not two. The output is wrong. The default behavior is the trap. Always write the frame clause explicitly.

The Postgres docs explicitly state the default is RANGE. So do the SQL Server docs. Most candidates default to writing no frame at all because they have never read the docs. State the frame out loud while you type it: 'ROWS, not RANGE, so duplicate dates do not collapse.' That single sentence is what separates the answer that ships from the answer that ships a silent bug.

Output trace

Walk through a small example. Account A has three transactions: $100 on Jan 1, $50 on Jan 2, $200 on Jan 5. Account B has two transactions: $300 on Jan 1, $-100 on Jan 3. After the window function runs, A gets running balances of 100, 150, 350. B gets 300, 200. The partition restart between A and B is what PARTITION BY guarantees. The fact that B's first day matches A's first day but they do not interfere is what PARTITION BY guarantees.
account_idtxn_dateamountrunning_balance
A2025-01-01100100
A2025-01-0250150
A2025-01-05200350
B2025-01-01300300
B2025-01-03-100200

The variant interviewers love: per-period running total with a reset

If the interviewer says 'year-to-date balance' instead of 'lifetime balance,' the window has to reset at the start of each year. Add the year to the PARTITION BY. Now the running total restarts when txn_date crosses into a new year.
/* Year-to-date balance per account */
SELECT
account_id,
txn_date,
amount,
SUM(amount) OVER (
PARTITION BY account_id, EXTRACT(YEAR FROM txn_date)
ORDER BY txn_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS ytd_balance
FROM transactions
TIP
When you change a running total from lifetime to year-to-date, the only thing that changes is the PARTITION BY. The frame clause stays UNBOUNDED PRECEDING to CURRENT ROW. The ORDER BY stays the same. Articulate that out loud: 'the only change is adding the year to the partition.' This shows you see the structure, not the syntax.

Rolling Windows (7-Day Trailing)

Daily Life
Interviews

Explain why RANGE (the default) groups duplicate ORDER BY values and produces wrong running totals, while ROWS does not.

This is the section the interviewer goes deepest on. The frame clause is where they probe whether you understand the silent bug that ships running totals to production with wrong numbers. The bug does not throw an error. It does not warn. It produces visibly plausible numbers that are subtly wrong. Anyone who has been on call for a reporting pipeline has seen this bug. Anyone who has not is about to learn it the hard way.
The silent bug, in two lines:
  • RANGE treats tied ORDER BY values as a peer group; ROWS treats them individually
  • Three transactions on the same day: RANGE returns 180, 180, 180; ROWS returns 100, 150, 180
  • Default is RANGE on every major engine
  • Fix is one word in the query: write ROWS explicitly

The bug, stated plainly

If two rows share the same ORDER BY value, RANGE treats them as a single peer group and assigns them the same cumulative value. ROWS treats them as individual rows and assigns each a distinct cumulative value. Most candidates have never seen this difference because their toy examples use unique dates. Real transaction data has many rows per day. The default behavior is RANGE. The right behavior is almost always ROWS.

Concrete example of the bug

Account A has three transactions on Jan 1: $100, $50, $30. The intended running total is 100, 150, 180. RANGE gives 180, 180, 180 because all three rows tie on txn_date. ROWS gives 100, 150, 180 because each row is its own step. The same query, the same data, two different answers. The wrong answer ships if you forget the ROWS keyword.
/* The default behavior (RANGE) is wrong for most cases */
SELECT
txn_date,
amount,
SUM(amount) OVER (
ORDER BY txn_date
) AS bad_running, /* 180, 180, 180 */
SUM(amount) OVER (
ORDER BY txn_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS good_running /* 100, 150, 180 */
FROM transactions
WHERE account_id = 'A'
RANGE (the silent default)
  • Ties on the ORDER BY column share a single cumulative value
  • Three transactions on the same day all get the same running total
  • Looks reasonable in a quick spot-check
  • Wrong when txn_date is the ORDER BY and multiple rows per day exist
ROWS (almost always what you want)
  • Every row gets its own cumulative value
  • Ties on the ORDER BY column are broken by row position
  • Matches what a business analyst means by 'running total'
  • Write this explicitly; do not rely on the default

Why RANGE exists at all

RANGE is correct when the ORDER BY column is intended to define peer groups. For example, 'cumulative number of distinct dates with any activity' is a RANGE-style question. But 99% of running-total questions in interviews are ROWS-style: a transaction is a transaction, even if two of them happened on the same calendar day. Defaulting to RANGE is the wrong default. SQL inherited it from a strict reading of the standard. Modern engines have not changed it for backward compatibility.

The interviewer at Meta and Stripe specifically watches for this. They will write three rows with the same date in the test data, run your query, and watch you not notice. If you write ROWS unprompted, you get a point. If you do not, and the interviewer says 'walk me through your output,' you have to find the bug live. Finding it is recoverable. Missing it after they hint is not.

At Meta in 2022, a monetization analytics team rebuilt a 'cumulative ad revenue per advertiser per day' dashboard after the original shipped a SUM OVER without an explicit ROWS frame. The bug surfaced when a top-10 advertiser ran a high-frequency programmatic campaign that filed 8,000 line items in a single hour, all second-truncated to the same timestamp. The dashboard reported the same cumulative figure for every one of those 8,000 rows. Three weeks of dashboard reads showed the wrong total before finance flagged it during quarterly close. The fix was one word; the rubric line the team wrote into their hiring loop was 'a candidate who writes SUM OVER without an explicit frame and cannot recover when asked about same-timestamp rows is a no-hire at this level.' The frame clause is the difference between syntax and signal.

Other frame clauses worth knowing

Once you know UNBOUNDED PRECEDING and CURRENT ROW, learn three more. ROWS BETWEEN 6 PRECEDING AND CURRENT ROW gives a 7-day trailing window. ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING gives a reverse running total (sum from here to the end). ROWS BETWEEN 3 PRECEDING AND 3 FOLLOWING gives a centered 7-row window. The first is the most common follow-up. The other two appear in advanced questions.
SituationPhrasing that flatlinesPhrasing that lands
You see 'cumulative balance per account'"SUM with a GROUP BY.""This is a running total. SUM OVER (PARTITION BY account_id ORDER BY txn_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW). One row per input row, not one per group."
The interviewer hands you ties on date"It still works.""With the default RANGE frame, ties on ORDER BY collapse into one peer-group value. ROWS gives each row its own cumulative value. I always write ROWS explicitly; the default is a silent bug."
The interviewer asks 'scale to a billion rows'"Window functions are fast.""Pre-aggregate to daily grain inside a CTE first, then run the window over the daily totals. The window cost is dominated by per-partition sort; pre-aggregation cuts the row count, and partition layout decides whether the sort is local or shuffled."
The interviewer asks 'how would you stream this'"Maintain state per account.""Streams hate running totals. I'd let a nightly batch compute history over the daily aggregate and have the stream handle current-day only; late arrivers reconcile next day. Pure-stream running totals require unbounded state per partition."
The interviewer asks for 'only positive transactions'"WHERE amount > 0.""WHERE filters rows out of the output, which changes the row count. SUM(CASE WHEN amount > 0 THEN amount END) keeps every row present and only contributes matching ones; SUM ignores NULL. 'Filter the input' versus 'filter the contribution' are different contracts."
TIP
When you write the frame clause, say it out loud in English. 'Rows between unbounded preceding and current row.' If you cannot read it as a sentence, you will get the syntax wrong. The ROWS BETWEEN ... AND ... structure is read left to right exactly as written.

Resetting the Total with PARTITION BY

Daily Life
Interviews

Extend running totals to 7-day rolling averages, monthly resets, and conditional cumulative sums.

Once the running total works, the interviewer will pivot. 'Now do a 7-day rolling sum.' 'Now reset every month.' 'Now only count positive transactions.' These variants test whether you understand the structure or just memorized the canonical query. Each variant changes exactly one part: the frame clause, the partition, or the source expression.

7-day rolling sum

Change the frame clause. UNBOUNDED PRECEDING becomes 6 PRECEDING. The window is now seven rows wide instead of growing without bound. The rest of the query is identical.
/* 7-day rolling sum, per account */
SELECT
account_id,
txn_date,
amount,
SUM(amount) OVER (
PARTITION BY account_id
ORDER BY txn_date
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) AS rolling_7d
FROM transactions

ROWS BETWEEN 6 PRECEDING AND CURRENT ROW is seven rows, not six. The frame is inclusive on both sides. Counting off by one here is a classic interview mistake. Read it out loud: '6 preceding through current, that's seven rows.'

Monthly reset

Change the partition. Add DATE_TRUNC('month', txn_date) to PARTITION BY. The running total now restarts at the first of each month.
/* Month-to-date balance per account */
SELECT
account_id,
txn_date,
amount,
SUM(amount) OVER (
PARTITION BY account_id, DATE_TRUNC('month', txn_date)
ORDER BY txn_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS mtd_balance
FROM transactions
Walk through the trace mentally. On Jan 31 the running balance reflects all of January's transactions. On Feb 1 it resets to whatever happened on Feb 1. The DATE_TRUNC produces a value that changes once per month, and PARTITION BY treats each truncated value as its own partition.

Conditional running totals

Sometimes the interviewer asks 'only count positive transactions toward the running total.' Wrap the column in a CASE expression. The SUM ignores NULLs, so anything you do not want to count, return NULL.
Three variants, three knobs:
  • 7-day rolling: change the frame clause to ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
  • Monthly reset: change the partition to add DATE_TRUNC('month', txn_date)
  • Conditional total: change the source expression to SUM(CASE WHEN ... THEN amount END)
/* Running total of only positive amounts */
SELECT
account_id,
txn_date,
amount,
SUM(
CASE
WHEN amount > 0 THEN amount
END
) OVER (
PARTITION BY account_id
ORDER BY txn_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS running_credits
FROM transactions
TIP
Conditional aggregates inside window functions are the move the interviewer is watching for. Most candidates use a WHERE clause to filter the rows first, which changes the row count of the output. The CASE-inside-SUM pattern keeps every row in the output but only includes matching rows in the cumulative sum. Same arithmetic, different shape; the interviewer can tell the difference at a glance.

The pattern, restated

Every running-total variant changes one of three things. The frame clause for window size (unbounded vs N preceding). The partition for the reset boundary (lifetime vs year vs month). The source expression for what counts (raw column vs CASE expression). Knowing this lets you answer any follow-up by adjusting one piece. When the interviewer asks 'now do X,' say out loud which of the three pieces you are changing. That meta-statement is what tells the interviewer you see the template, not the syntax.
Answer that flatlines
  • Rewrites the entire query from scratch for each variant
  • Has to recall the full syntax every time
  • Treats the variants as separate problems
  • Slow on follow-ups; runs out of time before the third probe
Answer that compounds
  • Identifies which of the three pieces needs to change
  • Changes that one piece and narrates why
  • Treats the canonical form as a template, not a problem
  • Handles four follow-ups in the time most candidates handle one

Same-Timestamp Rows and RANGE Surprises

Daily Life
Interviews

Discuss partition-level parallelism, pre-aggregation tradeoffs, and why running totals are expensive on streaming systems.

'How does this perform on a billion-row table?' is the standard scale follow-up. The right answer covers three things: how window functions parallelize, what pre-aggregation buys you, and why streaming systems struggle with running totals. Nailing all three flips the verdict from hire to strong hire on this question.

Window function parallelism

PARTITION BY is what makes window functions scale. Each partition is computed independently, so the engine can shard the workload across cores or executors. A query with PARTITION BY account_id on a billion-row table with 10 million accounts can be split across however many workers the engine has. The bottleneck is the per-partition sort: if any single account has 50 million transactions, that worker has to sort 50 million rows alone. Hot partitions are the scaling problem, not partition count.

Pre-aggregation when the grain is coarser than the source

If the question is 'daily running balance' and the source is per-transaction, pre-aggregate to daily first. Sum amount by (account_id, txn_date), then run the window function on the daily totals. A billion-row transaction table might collapse to a few hundred million daily rows. The cumulative sum then runs on fewer rows. The accuracy is identical because addition is associative.
/* Pre-aggregate, then cumulate */
WITH daily AS (
SELECT
account_id,
txn_date,
SUM(amount) AS daily_net
FROM transactions
GROUP BY account_id, txn_date
)
SELECT
account_id,
txn_date,
SUM(daily_net) OVER (
PARTITION BY account_id
ORDER BY txn_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS running_balance
FROM daily

Mention pre-aggregation unprompted. 'For scale, I'd pre-aggregate to daily grain inside a CTE, then run the running total on the daily totals.' This single sentence tells the interviewer you have built dashboards that ran nightly on real data and learned that you do not run window functions on raw event streams when the consumer wants daily numbers.

Why running totals are hard in streaming

Stream processors hate running totals. The cumulative value depends on every prior event in the partition, so the processor either has to keep unbounded state per partition or accept that late-arriving events corrupt downstream rows. Both are operationally painful. Most production systems solve this by computing the running total in a nightly batch over the daily aggregate, then serving it from a separate table. The streaming layer handles fresh events for the current day; the batch layer recomputes history. If the interviewer asks about real-time running totals, that is the answer they want to hear.

When the window function is the wrong tool

If the downstream consumer only needs the latest running total per account (not the full history per row), the window function is doing unnecessary work. A GROUP BY with a single SUM produces the same end-state value for a fraction of the cost. Reach for the window function when the consumer needs the running total at every intermediate point in time. Reach for GROUP BY when they only need the latest value. The interviewer will sometimes ask the question in a way that obscures which one is needed; clarify the consumer's actual need before reaching for SUM OVER. The judgment of when not to use the fancy tool is the move the interviewer is scoring.

Partitioned vs clustered table layouts

On Snowflake or BigQuery, the running-total query reads the daily aggregate table. How that table is laid out determines the cost. If the table is partitioned by account_id, every running-total query against a single account is a partition prune and reads almost no data. If the table is partitioned by date instead, the same query has to scan all dates for the account, which is expensive. The right layout depends on the query mix: if dashboards always query per-account, partition by account; if reports query per-date, partition by date; if both, cluster by one and partition by the other. Mention the layout tradeoff unprompted when the interviewer brings up scale; the answer separates someone who has tuned a warehouse from someone who has only queried one.
Naive streaming
  • Keep unbounded state per partition in the stream processor
  • Memory grows linearly with active accounts
  • Late-arriving events require restating downstream rows
  • Pages on-call when an account has too much history
Production pattern
  • Compute running totals in a nightly batch over daily aggregates
  • Stream layer handles current-day, batch layer handles history
  • Memory bounded by the daily window, not by account history
  • Late-arrivers are reconciled the next day, not in real time

The closing summary for this question

Close with the three-sentence wrap that hits every rubric item. 'I used SUM OVER with PARTITION BY account, ORDER BY date, and an explicit ROWS frame from unbounded preceding to current row. For scale, I'd pre-aggregate to daily grain before the window function and materialize the result nightly. In streaming, I'd avoid running totals entirely and let a batch layer compute history while the stream handles the current day.' Three sentences. Algorithm, scale, system design. That answer turns what started as a basic SQL screen into a full system-design conversation.
PUTTING IT ALL TOGETHER

> You are in a Databricks data engineering interview. The interviewer asks: 'Compute month-to-date revenue per region per product, with a 7-day rolling component, on a billion-row transactions table.'

You ask first: 'What grain does the dashboard need - daily, hourly, or per-transaction?' The interviewer says daily.
You pre-aggregate in a CTE: SUM(amount) GROUP BY region, product, txn_date inside daily_agg.
You compute month-to-date in the outer query: SUM OVER PARTITION BY (region, product, DATE_TRUNC('month', txn_date)) ORDER BY txn_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW.
You add the 7-day rolling as a sibling expression with ROWS BETWEEN 6 PRECEDING AND CURRENT ROW over the same partition.
Closing: 'On a billion-row source the dominant cost is the partition shuffle on (region, product). If this query runs hourly, I would cluster the source table by region and accept the in-partition sort.'
KEY TAKEAWAYS
The canonical running total is SUM(amount) OVER (PARTITION BY account_id ORDER BY txn_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), and each clause carries weight: partition sets the reset, order sets the sequence, frame sets the window.
Omitting the frame clause defaults to RANGE, which gives every row tied on the ORDER BY column the same cumulative value: three same-day transactions of 100, 50, and 30 report 180, 180, 180 instead of 100, 150, 180.
Every variant changes exactly one knob: the frame for window size (6 PRECEDING for a 7-row trailing sum), the partition for the reset boundary (add DATE_TRUNC('month', txn_date)), or the source expression for what counts (a CASE that returns NULL for rows to skip).
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW is seven rows because the frame is inclusive on both ends, and the off-by-one here is a routine interview miss.
At scale, pre-aggregate to the consumer's grain inside a CTE before the window function, since addition is associative and a billion transaction rows can collapse to a few hundred million daily rows; PARTITION BY is what lets the engine shard the work, so hot partitions are the real ceiling, not partition count.
Reach for GROUP BY when the consumer only needs the latest cumulative value per account, and keep the window function for when they need it at every intermediate point in time.

The cumulative sum that trips up candidates who forget frame clauses

Category
SQL
Difficulty
intermediate
Duration
25 minutes
Challenges
0 hands-on challenges

Topics covered: SUM OVER with the Right Frame, ROWS vs RANGE: The Silent Bug, Rolling Windows (7-Day Trailing), Resetting the Total with PARTITION BY, Same-Timestamp Rows and RANGE Surprises

Lesson Sections

  1. SUM OVER with the Right Frame (concepts: sqlWindowFrame)

    Here is the most common shape this question takes. The interviewer hands you a transactions table and says 'compute the running balance per account over time.' Two minutes later they have a sense of where you sit on the depth axis. Not from your syntax, but from how cleanly you say what you are doing. The answer that lands names the pattern, names the frame clause, and writes the query in twelve lines. The answer that flatlines writes the same twelve lines but cannot explain why the frame clause

  2. ROWS vs RANGE: The Silent Bug (concepts: sqlWindowFrame)

    The canonical running total query is the one you should be able to write while talking. PARTITION BY the grouping column, ORDER BY the time column, frame clause UNBOUNDED PRECEDING to CURRENT ROW. The frame clause is what makes it a running total rather than a partition-wide aggregate. Why each clause matters PARTITION BY account_id: the running total resets at every new account. Without it, the SUM runs across every transaction in the table, regardless of which account it belongs to. ORDER BY t

  3. Rolling Windows (7-Day Trailing) (concepts: sqlWindowFrame)

    This is the section the interviewer goes deepest on. The frame clause is where they probe whether you understand the silent bug that ships running totals to production with wrong numbers. The bug does not throw an error. It does not warn. It produces visibly plausible numbers that are subtly wrong. Anyone who has been on call for a reporting pipeline has seen this bug. Anyone who has not is about to learn it the hard way. The bug, stated plainly If two rows share the same ORDER BY value, RANGE t

  4. Resetting the Total with PARTITION BY (concepts: sqlWindowFrame)

    Once the running total works, the interviewer will pivot. 'Now do a 7-day rolling sum.' 'Now reset every month.' 'Now only count positive transactions.' These variants test whether you understand the structure or just memorized the canonical query. Each variant changes exactly one part: the frame clause, the partition, or the source expression. 7-day rolling sum Change the frame clause. UNBOUNDED PRECEDING becomes 6 PRECEDING. The window is now seven rows wide instead of growing without bound. T

  5. Same-Timestamp Rows and RANGE Surprises (concepts: sqlAggregateOver)

    'How does this perform on a billion-row table?' is the standard scale follow-up. The right answer covers three things: how window functions parallelize, what pre-aggregation buys you, and why streaming systems struggle with running totals. Nailing all three flips the verdict from hire to strong hire on this question. Window function parallelism PARTITION BY is what makes window functions scale. Each partition is computed independently, so the engine can shard the workload across cores or executo