The answer is to use 1 PRECEDING, not CURRENT ROW -1. So, in your query, use:

    , SUM(s.OrderQty) OVER (PARTITION BY  SalesOrderID 
                            ORDER BY SalesOrderDetailID
                            ROWS BETWEEN UNBOUNDED PRECEDING 
                                     AND 1 PRECEDING) 
    AS  PreviousRunningTotal

Also note that on your other calculation:

    , SUM(s.OrderQty) OVER (PARTITION BY  SalesOrderID
                            ORDER BY SalesOrderDetailID) ...

SQL-Server uses the default * RANGE UNBOUNDED PRECEDING AND CURRENT ROW. I think that there is an efficiency difference and ROWS UNBOUNDED PRECEDING AND CURRENT ROW is to be preferred (after testing of course and if it gives the results you want).

Much more details you can find in the blog article by @Aaron Bertrand, including performance tests: Best approaches for running totals – updated for SQL Server 2012

* this is of course the default range when an ORDER BY is present inside the OVER clause - otherwise, without ORDER BY the default is the whole partition.

Answer from ypercubeᵀᴹ on Stack Exchange
🌐
SQLServerCentral
sqlservercentral.com › home › topics › running sum w window function: is rows between... clause required?
Running Sum w Window Function: is ROWS BETWEEN... clause required? – SQLServerCentral Forums
December 30, 2020 - DROP TABLE IF EXISTS dbo.running_total; ... BY MeasurementDateTime ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) As RunSum_WITH_Unbounded FROM running_total ORDER BY MeasurementPoint, MeasurementDateTime; When I run this (SQL Server 2019 Developer Edition), I get back identical ...
Discussions

sql - Calculate running total without using Rows Unbounded Preceding in 2008 - Stack Overflow
I have the below query that can calculate a running total in SQL Server 2016 Select BranchNo ,FiscalWeek ,SalesExVAT ,Sum(SalesExVAT) Over (Partition By BranchNo Order By FiscalWee... More on stackoverflow.com
🌐 stackoverflow.com
September 8, 2017
sql - What is ROWS UNBOUNDED PRECEDING used for in Teradata? - Stack Overflow
One end is fixed, the other relative ... e.g. a Running Total, Remaining Sum · Starting and ending row are relative to current row, the number of rows within a window is fixed, e.g. a Moving Average over n rows · So SUM(x) OVER (ORDER BY col ROWS UNBOUNDED PRECEDING) results in ... More on stackoverflow.com
🌐 stackoverflow.com
September 19, 2018
windows function with rows unbounded preceding
The default is RANGE UNBOUNDED PRECEDING which is mostly the same thing except in cases where the current row's value has "ties" so to speak. More on reddit.com
🌐 r/SQL
26
2
September 3, 2024
Cumulative sum or running sum
I think for it to be a running Sum you need ORDER BY in your parenthesis. SUM(COLUMN) OVER (ORDER BY productid) More on reddit.com
🌐 r/SQL
11
5
March 10, 2023
🌐
LearnSQL.com
learnsql.com › blog › sql-window-functions-rows-clause
5 Practical Examples of Using ROWS BETWEEN in SQL | LearnSQL.com
running total). Here’s the query we can use: SELECT date, revenue, SUM(revenue) OVER ( ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) running_total FROM sales ORDER BY date;
🌐
Oracle
blogs.oracle.com › sql › cumulative-running-total-of-previous-rows-with-sql
How to get the cumulative running total of previous rows with SQL
March 2, 2023 - To calculate the running total for a column you can use analytic functions, aka window functions. The basic syntax is: sum ( col ) over ( order by sort_col rows unbounded preceding )
🌐
Guillaume Martin
guillaume-martin.github.io › sql-running-total.html
Calculate a running total in SQL - Guillaume Martin
March 25, 2021 - WITH daily_amount AS ( SELECT payment_date::date , SUM(amount) AS amount FROM payment GROUP BY payment_date::date ) SELECT payment_date::date , amount , SUM(amount) OVER(ORDER BY payment_date ROWS UNBOUNDED PRECEDING) AS running_total FROM daily_amount ;
🌐
SimpleSQLTutorials
simplesqltutorials.com › home › how to get a running total of your sql data: explained for beginners
How To Get a Running Total of Your SQL Data: Explained for Beginners
June 30, 2023 - That’s what ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW means. The resulting summation is what is used as the ‘Running Total‘ value for that row. And actually, the delimiter ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENCT ROW is so common that SQL Server introduced a shorthand way to write it: ROWS UNBOUNDED PRECEDING
Find elsewhere
🌐
Medium
medium.com › @sahaabhik9 › unbounded-clause-in-sql-5c9750f02421
UNBOUNDED Clause in SQL. Though seldom used, the UNBOUNDED… | by Abhik Saha | Medium
December 7, 2024 - The UNBOUNDED PRECEDING keyword indicates that the window starts at the first row of the partition, while the UNBOUNDED FOLLOWING keyword indicates that the window ends at the last row of the partition.
🌐
SQLNerds
sqlnerds.com › learn › window-functions › understanding-rows-between-unbounded-preceding-and-current-row
Understanding ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW | SQLNerds
July 5, 2025 - Learn how to use the window frame clause ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW to create running totals or cumulative sums in PostgreSQL.
🌐
SQL with Manoj
sqlwithmanoj.com › tag › unbounded-preceding
UNBOUNDED PRECEDING – SQL with Manoj
February 1, 2013 - -- To Calculate Cumulative SUM or Running Totals, but in REVERSE order: ;WITH CTE AS ( SELECT BusinessEntityID AS SalesPersonID, CAST([Rate] AS DECIMAL(10,0))AS Salary, [ModifiedDate] AS SalDate FROM [HumanResources].[EmployeePayHistory] WHERE BusinessEntityID <= 10 ) SELECT SalesPersonID, SalDate, Salary ,SUM(Salary) OVER (ORDER BY SalesPersonID ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS ReverseCumulativeSumByRows ,SUM(Salary) OVER (ORDER BY SalesPersonID RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS ReverseCumulativeSumByRange FROM CTE ORDER BY SalesPersonID, SalDate ... In
🌐
C# Corner
c-sharpcorner.com › blogs › rowsunbounded-preceding-and-rows-unbounded-following
RowsUnbounded Preceding And Rows Unbounded Following
July 4, 2025 - SELECT Office_Expense_DID, Claim_Amount, SUM(Claim_Amount) OVER (ORDER BY Office_Expense_DID ROWS UNBOUNDED PRECEDING) AS CumulativeAmount FROM Imprest_OfficeExpense_Details ORDER BY Office_Expense_DID; SELECT Office_Expense_DID, Claim_Amount, SUM(Claim_Amount) OVER (ORDER BY Office_Expense_DID ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS CumulativeAmount FROM Imprest_OfficeExpense_Details ORDER BY Office_Expense_DID;
🌐
GeeksforGeeks
geeksforgeeks.org › sql › sql-rows-between
SQL - ROWS BETWEEN - GeeksforGeeks
July 23, 2025 - Step 2: Let's consider we want to calculate the total amount of salary till the current employee . Here we are going to see how to get the running salary total for each employee . ... SELECT * , sum(SALARY) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) as RUNNING_SUM FROM department
Top answer
1 of 2
186

It's the "frame" or "range" clause of window functions, which are part of the SQL standard and implemented in many databases, including Teradata.

A simple example would be to calculate the average amount in a frame of three days. I'm using PostgreSQL syntax for the example, but it will be the same for Teradata:

WITH data (t, a) AS (
  VALUES(1, 1),
        (2, 5),
        (3, 3),
        (4, 5),
        (5, 4),
        (6, 11)
)
SELECT t, a, avg(a) OVER (ORDER BY t ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING)
FROM data
ORDER BY t

... which yields:

t  a  avg
----------
1  1  3.00
2  5  3.00
3  3  4.33
4  5  4.00
5  4  6.67
6 11  7.50

As you can see, each average is calculated "over" an ordered frame consisting of the range between the previous row (1 preceding) and the subsequent row (1 following).

When you write ROWS UNBOUNDED PRECEDING, then the frame's lower bound is simply infinite. This is useful when calculating sums (i.e. "running totals"), for instance:

WITH data (t, a) AS (
  VALUES(1, 1),
        (2, 5),
        (3, 3),
        (4, 5),
        (5, 4),
        (6, 11)
)
SELECT t, a, sum(a) OVER (ORDER BY t ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
FROM data
ORDER BY t

yielding...

t  a  sum
---------
1  1    1
2  5    6
3  3    9
4  5   14
5  4   18
6 11   29

Here's another very good explanations of SQL window functions.

2 of 2
110

ROWS UNBOUNDED PRECEDING is no Teradata-specific syntax, it's Standard SQL. Together with the ORDER BY it defines the window on which the result is calculated.

Logically a Windowed Aggregate Function is newly calculated for each row within the PARTITION based on all ROWS between a starting row and an ending row.

Starting and ending rows might be fixed or relative to the current row based on the following keywords:

  • CURRENT ROW, the current row
  • UNBOUNDED PRECEDING, all rows before the current row -> fixed
  • UNBOUNDED FOLLOWING, all rows after the current row -> fixed
  • x PRECEDING, x rows before the current row -> relative
  • y FOLLOWING, y rows after the current row -> relative

Possible kinds of calculation include:

  • Both starting and ending row are fixed, the window consists of all rows of a partition, e.g. a Group Sum, i.e. aggregate plus detail rows
  • One end is fixed, the other relative to current row, the number of rows increases or decreases, e.g. a Running Total, Remaining Sum
  • Starting and ending row are relative to current row, the number of rows within a window is fixed, e.g. a Moving Average over n rows

So SUM(x) OVER (ORDER BY col ROWS UNBOUNDED PRECEDING) results in a Cumulative Sum or Running Total

11 -> 11
 2 -> 11 +  2                = 13
 3 -> 13 +  3 (or 11+2+3)    = 16
44 -> 16 + 44 (or 11+2+3+44) = 60
🌐
Reddit
reddit.com › r/sql › windows function with rows unbounded preceding
r/SQL on Reddit: windows function with rows unbounded preceding
September 3, 2024 -

Hi,

Is rows unbounded preceding the default behavior of a windows function with an order by?

Because they both calculate a running aggregate function from the start until the current row**.**

That is, the 2 queires below are the same

select 
user_id,
SUM(tweet_count) OVER(PARTITION BY user_id ORDER BY tweet_date 
      ROWS unbounded preceding) as mysum
from tweets;

select 
user_id,
SUM(tweet_count) OVER(PARTITION BY user_id ORDER BY tweet_date) as mysum
from tweets;
🌐
Sqlperformance
sqlperformance.com › home › best approaches for grouped running totals
Best approaches for grouped running totals - SQLPerformance.com
June 30, 2014 - SELECT LicenseNumber, IncidentDate, TicketAmount, RunningTotal = SUM(TicketAmount) OVER ( PARTITION BY LicenseNumber ORDER BY IncidentDate RANGE UNBOUNDED PRECEDING ) FROM dbo.SpeedingTickets ORDER BY LicenseNumber, IncidentDate;
🌐
MSSQLTips
mssqltips.com › home › simple way to calculate running totals in sql server
Simple Way to Calculate Running Totals in SQL Server
March 6, 2026 - By default, SQL uses a frame known as RANGE, which groups together rows with the same ORDER BY value rather than focusing on their row position. I’ve listed the default frame below. /* MSSQLTips.com */ SELECT TurtleName, DateEaten, SlicesEaten, SUM(SlicesEaten) OVER (PARTITION BY TurtleName ORDER BY DateEaten RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS RunningTotal ...
🌐
MySQL
dev.mysql.com › doc › refman › 8.0 › en › window-functions-frames.html
MySQL :: MySQL 8.0 Reference Manual :: 14.20.3 Window Function Frame Specification
The following query demonstrates ... and the rows that immediately precede and follow it: mysql> SELECT time, subject, val, SUM(val) OVER (PARTITION BY subject ORDER BY time ROWS UNBOUNDED PRECEDING) AS running_total, AVG(val) OVER (PARTITION BY subject ORDER BY time ROWS BETWEEN ...
🌐
YouTube
youtube.com › shorts › ClgKMq5_WW4
How does “ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW” create this running total in #SQL - YouTube
Enjoy the videos and music you love, upload original content, and share it all with friends, family, and the world on YouTube.
Published: November 2, 2025