with cte as
 (
   select *,
      -- find the latest 'V' ID per ArtNo
      max(case when Flag = 'V' then ID end) 
      over (partition by ArtNo) as Last_V_ID
   from myTable
 )
select *,
   -- cumulative sum, but ignore all rows before the latest 'V' ID
   -- includes rows when there's no 'V' ID for this ArtNo
   sum(case when ID < Last_V_ID then null else Amount end)
   over (partition by ArtNo
         order by ID
         rows unbounded preceding)
from cte
order by ArtNo, ID

See Fiddle

Edit:

To include the data before the last stocktaking and to ignore all previous stocktakings you can use this approach:

with cte as
 (
   select *,
      -- find the latest 'V' ID per ArtNo
      max(case when Flag = 'V' then ID end) 
      over (partition by ArtNo) as Last_V_ID
   from [dbo].[Warehouse]
 )
select *,
   -- cumulative sum, but ignore all rows before the latest 'V' ID
   -- includes rows when there's no 'V' ID for this ArtNo
   sum(case when ID < Last_V_ID then null else Amount end)
   over (partition by ArtNo
         order by ID
         rows unbounded preceding)
   -- calculate in-stock based on last 'V' ID, discarding all previous 'V' rows
  ,sum(case when (ID < Last_V_ID and Flag <> 'V')  then -Amount 
            when ID = Last_V_ID then Amount 
       end)
   over (partition by ArtNo
         order by ID 
         rows between 1 following and unbounded following)
from cte
order by ArtNo, ID

Both calculations are mutually exlusive, so you can easily combine them using COALESCE.

See Fiddle

Answer from dnoeth on Stack Overflow
🌐
Microsoft Learn
learn.microsoft.com › en-us › answers › questions › 783234 › conditional-running-total-sql
Conditional Running Total sql - Microsoft Q&A
DROP TABLE RESULTTABLE CREATE TABLE RESULTTABLE (Datestr varchar(20), Rejection int, Days int, RUNNINGTOTAL int ) DECLARE @DATESTR VARCHAR(20) DECLARE @REJECTION INT DECLARE @DAYS INT DECLARE @SUM INT =0 DECLARE db_cursor CURSOR FOR SELECT Datestr,Rejection, Days FROM TEMPTABLE OPEN db_cursor FETCH NEXT FROM db_cursor INTO @DATESTR,@REJECTION, @DAYS WHILE @@FETCH_STATUS = 0 BEGIN IF @REJECTION = 0 BEGIN SET @SUM = @SUM+@DAYS END ELSE BEGIN SET @SUM =@DAYS/(@REJECTION+1) END INSERT INTO RESULTTABLE SELECT @DATESTR,@REJECTION,@DAYS,@SUM FETCH NEXT FROM db_cursor INTO @DATESTR,@REJECTION, @DAYS END CLOSE db_cursor DEALLOCATE db_cursor SELECT * FROM RESULTTABLE
Discussions

sql server - Running total until specific condition is met - Database Administrators Stack Exchange
I have a question about running totals, I know there are several approaches. However I have slight modification of it and I'm struggling to find the right way. So I have orders, each order has its More on dba.stackexchange.com
🌐 dba.stackexchange.com
May 6, 2020
SQL Conditional select - calculate running total - Stack Overflow
I have a stored procedure that calculates requirements for customers based on input that we receive from them. Displaying this information is not a problem. What I'd like to do is show the most r... More on stackoverflow.com
🌐 stackoverflow.com
Running total with reset condition – SQLServerCentral Forums
Category Value Running_total groupId 101 10 10 1 102 20 30 1 103 30 30 2 104 12 12 3 105 20 20 4 106 5 25 4 107 10 10 5 ... Are there other values in the set? You don't really have anything that creates a group, other than the sum, and for that, you have a variable set of values. ... Here's a version that works in all versions of SQL ... More on sqlservercentral.com
🌐 sqlservercentral.com
September 11, 2017
sql server - Conditional running total across columns in SQL - Stack Overflow
I have this data: Player StartBalance Day1Earned Day1Spent Day2Earned Day2Spent Day3Earned Day3Spent Alex 10 0 0 3 -5 3 ... More on stackoverflow.com
🌐 stackoverflow.com
🌐
Microsoft
social.msdn.microsoft.com › Forums › sqlserver › en-US › b5ce0180-7e38-435b-9b86-46e1bc9ffa45 › conditional-running-total
Conditional Running Total
It's not a resetting running total though. Let me know if that helps. You could also add the column in the first outer apply to the select clause to see the begin month being identified for each row. I typically include output to all outer applies when I'm developing a query, then trim as needed. ... In SQL 2008 and prior versions using loop actually performs better than set-based solutions if CLR is not an option.
🌐
Stack Overflow
stackoverflow.com › questions › 26062428 › sql-conditional-select-calculate-running-total
SQL Conditional select - calculate running total - Stack Overflow
This method works ok as long as the LastReceivedQty is less than the Day1 requirements but it's incorrect because it allows a negative number to be displayed in day one rather than pulling the remainder from day2.
🌐
SQLServerCentral
sqlservercentral.com › home › topics › running total with reset condition
Running total with reset condition – SQLServerCentral Forums
September 11, 2017 - --===== The table MUST have a UNIQUE CLUSTERED INDEX -- on the Category column AND the Category column -- MUST preserve the order that you want the data -- to appear in (might be tough with VARCHAR()). CREATE TABLE #TestTable ( Category VARCHAR(5) NOT NULL PRIMARY KEY CLUSTERED ,Value INT NOT NULL ,RunningTotal INT ,GroupID INT ) ; INSERT INTO #TestTable (Category, Value) SELECT '101', 10 UNION ALL SELECT '102', 20 UNION ALL SELECT '103', 30 UNION ALL SELECT '104', 12 UNION ALL SELECT '105', 20 UNION ALL SELECT '106', 5 UNION ALL SELECT '107', 10 ; After that, we do what is affectionately known as the "Quirky Update". It works in all versions of SQL from 2005 and up.
🌐
Blogger
mssqlhelp.blogspot.com › 2011 › 11 › conditional-running-total.html
Microsoft SQL Server Help: Conditional Running Total
( account varchar(6), mnth int, paid_indicator tinyint ) insert into @data(account,mnth,paid_indicator) values ('ABC123',201101,1), ('ABC123',201102,0), ('ABC123',201103,1), ('ABC123',201104,1), ('ABC123',201105,0), ('ABC123',201106,0), ('ABC123',201107,1), ('ABC123',201108,1), ('ABC123',201109,1), ('ABC123',201110,1), ('ABC123',201111,1), ('ABC123',201112,0), ('ABC123',201201,1), ('ZZZ555',201105,1), ('ZZZ555',201106,1), ('ZZZ555',201107,0), ('ZZZ555',201108,0), ('ZZZ555',201109,0), ('ZZZ555',201110,1) -- Running Total select d.*, isnull(rt.rt,0) as running_total from @data d outer apply
Find elsewhere
🌐
Stack Overflow
stackoverflow.com › questions › 5337837 › conditional-running-total-across-columns-in-sql
sql server - Conditional running total across columns in SQL - Stack Overflow
I have this data: Player StartBalance Day1Earned Day1Spent Day2Earned Day2Spent Day3Earned Day3Spent Alex 10 0 0 3 -5 3 ...
🌐
LearnSQL.com
learnsql.com › blog › what-is-a-running-total-and-how-to-compute-it-in-sql
What Is a Running Total and How Do You Compute It in SQL? | LearnSQL.com
The syntax of the SQL window function that computes a cumulative sum across rows is: window_function ( column ) OVER ( [ PARTITION BY partition_list ] [ ORDER BY order_list] ) It’s mandatory to use the OVER clause in a window function, but the arguments in this clause are optional. We will discuss them in the next paragraphs of this article. In this example, we will calculate the total running sum of the registered users each day.
🌐
Microsoft Fabric Community
community.fabric.microsoft.com › t5 › Desktop › How-to-calculate-running-total-by-row-with-a-conditional-reset › m-p › 815812
How to calculate running total by row with a conditional reset
August 31, 2021 - The reset condition is based on IDICATOR column. If it states NO then the normal running total on AMONT should be performed but if it states YES then the calculation should show a 0; for each row that states YES.
🌐
GeeksforGeeks
geeksforgeeks.org › sql › calculate-running-total-in-sql
Calculate Running Total in SQL - GeeksforGeeks
November 17, 2025 - SELECT * ,( SELECT SUM(T2.[SALARY]) FROM [department] AS T2 WHERE T2.[ID] <= T1.[ID] ) AS [Running Total] FROM [department] AS T1 ... Example 2 In this SQL Server example, we'll use the SUM Function and OVER to find the Running Total.
🌐
Interview Query
interviewquery.com › p › sql-cumulative-sum-guide
SQL Cumulative SUM: Window Functions, Rolling Totals & Best Practices
March 17, 2026 - By using a CASE WHEN expression inside your cumulative query, you can introduce flags, thresholds, or conditional counters that adapt to business rules. For example, you might track how many promo orders a customer has placed so far or flag the first time their lifetime spend crosses $1,000. This pattern is widely used in reporting because it naturally supports scenarios like a SQL cumulative count of specific events or building thresholds with a SQL CASE WHEN with cumulative sum. In practice, it lets you move beyond a simple running total and create tailored cumulative metrics that answer deeper business questions.
Top answer
1 of 2
1

There's no efficient solution using plain SQL (including Windowed Aggregate Functons), at least nobody found one, yet :-)

Your recursive query performs bad because it's way too complicated, this is a simplified version:

Edit: Fixed the calculation (Fiddle)

WITH ctePoints AS
 (
   SELECT 1 AS id
        ,rank
        ,CASE 
           WHEN rank >= 10 THEN 10
           WHEN rank = 1 THEN 11
           ELSE rank
         END AS Point
        ,1 AS Counter
   FROM dbo.BlackJack
   WHERE Id = 1

   UNION ALL

   SELECT t2.Id
        ,t2.rank
        ,CASE WHEN t1.Point < 17 THEN t1.Point ELSE 0 END 
         + CASE 
             WHEN t2.rank >= 10 THEN 10
             WHEN t2.rank = 1 THEN 11
             ELSE t2.rank
           END AS Point
        ,CASE WHEN t1.Point < 17 THEN t1.Counter + 1 ELSE 1 END AS Counter
   FROM dbo.BlackJack AS t2
   INNER JOIN ctePoints AS t1 ON t2.Id = t1.Id + 1
 ) 
SELECT ctepoints.*
     ,CASE 
        WHEN Point < 17 THEN ''
        WHEN Point < 20 THEN 'S'
        WHEN Point > 21 THEN 'L'
        WHEN Point = 21 AND Counter = 2 THEN 'B'
        ELSE 'W' 
      END AS DealerStatus            
FROM ctePoints

It's probably still too slow, because it processes row by row.

I usually use recursive SQL to replace cursor logic (because in my DBMS it's usually much faster) but a cursor update might actually be faster (Demo):

CREATE TABLE #BlackJack
(
   id INT PRIMARY KEY CLUSTERED
  ,Rank INT
  ,DealerStatus CHAR(1)
);

insert into #BlackJack (Id, Rank)
values 
(1, 1),(2, 5), (3, 8), (4, 3), (5, 1), (6, 7), (7, 10), (8, 1),(9, 10), (10, 10), (11,1);


DECLARE @Counter INT = 0
        ,@Point INT = 0
        ,@id int
        ,@Rank int
        ,@DealerStatus char(1)

DECLARE c CURSOR
FOR
SELECT id, Rank
FROM #BlackJack 
ORDER BY id FOR UPDATE OF DealerStatus

OPEN c

FETCH NEXT FROM c INTO @id, @Rank

WHILE @@FETCH_STATUS = 0
  BEGIN
    SET @counter = @counter + 1

    SET @Rank = CASE
                  WHEN @Rank >= 10 THEN 10
                  WHEN @Rank  = 1  THEN  11
                  ELSE @Rank
                END 

    SET @Point = @Point + @Rank

    SET @DealerStatus = CASE 
                          WHEN @Point < 17 THEN ''
                          WHEN @Point < 20 THEN 'S'
                          WHEN @Point > 21 THEN 'L'
                          WHEN @Point = 21 AND @Counter = 2 THEN 'B'
                          ELSE 'W' 
                        END 

    IF @Point >= 17 
    BEGIN
      UPDATE  #BlackJack 
      SET DealerStatus = @DealerStatus
      WHERE CURRENT OF c;

      SET @Point = 0

      SET @Counter = 0
    END

    FETCH NEXT FROM c INTO @id, @Rank
  END

CLOSE c
DEALLOCATE c

SELECT * FROM #BlackJack ORDER BY id

Still @lad2025's "quirky update" is the fastest way to get the expected result, but it's using an undocumented feature and if a Service Pack breaks it there's no way to complain about it :-)

2 of 2
1

This solution is based on quirky update. More info here.

LiveDemo

Data and structures:

CREATE TABLE #BlackJack
(
   id INT 
  ,Rank INT
  ,running_total INT
  ,result NVARCHAR(100)
);

CREATE CLUSTERED INDEX IX_ROW_NUM ON #BlackJack(id);

insert into #BlackJack (Id, Rank)
values (1, 1),(2, 5), (3, 8), (4, 3), (5, 1),
       (6, 7), (7, 10), (8, 1),(9, 10), (10, 10), (11,1);

Main query:

DECLARE @running_total       INT = 0
        ,@number_of_cards    INT = 0
        ,@prev_running_total INT = 0;

UPDATE #BlackJack
SET 
   @prev_running_total = @running_total
  ,@running_total = running_total = IIF(@running_total >= 20, 0, @running_total) 
                                    + CHOOSE(Rank,11,2,3,4,5,6,7,8,9,10,10,10,10)
  ,result        = CASE WHEN @running_total = 20 THEN 'S'
                        WHEN @running_total = 21 AND @number_of_cards = 2 THEN 'B'
                        WHEN @running_total = 21 THEN 'W'
                        WHEN @running_total > 21 THEN 'L'
                        ELSE NULL
                    END
  ,@number_of_cards  = IIF(@prev_running_total >= 20, 0, @number_of_cards) + 1
FROM #BlackJack WITH(INDEX(IX_ROW_NUM))
OPTION (MAXDOP 1);

SELECT *
FROM #BlackJack
ORDER BY id;

Warning

If you use SQL Server < 2012 you need to replace IIF and CHOOSE with CASE. I don't check all Blackjack rules, only for provided sample. If something is wrong feel free to change CASE logic.

Second I extend base table BlackJack with auxiliary columns, but you can create any new table, if needed.

The key point is to read data sequentially based on clustered key ascending and do not allow parallel execution. Before you use it in production check how it behaves with large data set.

🌐
Microsoft Q&A
social.msdn.microsoft.com › forums › sqlserver › en-US › afc801e6-f078-49a3-bb18-10660aa94a09 › conditional-count-or-conditional-sum-or-running-total
Conditional count Or Conditional Sum Or Running Total
In order to calculate and display the cumulative sum conditionally as the example you posted, we can use the expression like =IIf( ... In the footer, we can use expression to get the last running value like =RunningValue(IIf(Fields!Field1.Value=”cancelled”,1,0),Sum,"DataSet1") Please correct ...
Top answer
1 of 5
1

Just wondering if better if you had a date table driving this and join your transaction table to it then you should be able to use Sum Partition By for each previous 12 months and count where Transaction <> 0... what if you join below to the table.

The other guys on this site will probably know for sure if this would work.

with years as (
     select * from 
     (values(2006),(2007),(2008),(2009),(2010),(2011),(2012),(2013),(2014),(2015),(2016),(2017),(2018),(2019)
     ) as t (Year_id))
,months as (
     select * from 
     (values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
     ) as t (month_id))
select Year_id,month_id,0 as [Transaction_totals]
from years
cross join months
order by 1,2
2 of 5
0

Going with the suggestion by Roger Clerkwell, I would first create a Dates table using a With Block (CTE) to then use in the remainder of your query. This would allow you to query the data based on the dates being pulled back from your dates table.

Since I work out of an Oracle Database, my solution shows how to create a dates table that produces a list of month start dates for the date range entered into the Query. It can also produce month END dates, however, I realize for this question the Month End days are not needed. I've written a countless number of reports where I have used this. With some small tweaks, this code can also produce distinct days if you are needing to query results day by day as well as all sorts of other helpful hacks once you fully understand the CONNECT BY LEVEL.

SELECT TRUNC(ADD_MONTHS('01-JUL-18', LEVEL-1), 'MM') START_DATE,
       LAST_DAY(ADD_MONTHS('01-JUL-18', LEVEL-1)) END_DATE
FROM DUAL CONNECT BY LEVEL <= CEIL(MONTHS_BETWEEN('30-JUN-19', '01-JUL-18'))
;

The code will produce results that look like the below screenshot.

SQL Server, however, does not need to utilize a dual table. See THIS Stackoverflow question if you have questions about the dual table.

🌐
Essential SQL
essentialsql.com › home › use sql to calculate a running total
Use SQL to Calculate a Running Total - Essential SQL
March 4, 2023 - The easiest condition to understand is where we match TransactionDate. This ensures the invoices match have a common transaction date. If this was the only join we did we would be calculating a sub total for all transactions within a date. ... Since we want to calculate the running total, we need to somehow obtain for each InvoiceID the TransactionAmount for the invoice and all invoices before it.