Running Totals

  • I am having problems figuring out how to add a running total. I initially tried to get my TempTable to show the different categories of transactions by reporting 1, 2, or 3. I could not get my program to do this. I ultimately want curItem compared to prevItem and if there is not a match, to populate the Trnd field with 1. If the curItem and prevItem match, I need the program to incrementally sum from the first mis-match to the end of the matched fields.

    --===== If the test table already exists, drop it

    IF OBJECT_ID('TempDB..#mytable','U') IS NOT NULL

    DROP TABLE #mytable

    --===== Create the test table with

    CREATE TABLE #mytable

    (

    curItemchar,

    prevItemchar,

    Trndint

    )

    INSERT INTO #mytable

    (curItem, prevItem, Trnd)

    SELECT'D','U','' UNION ALL

    SELECT'D','U',''UNION ALL

    SELECT'U','D',''UNION ALL

    SELECT'U','U',''UNION ALL

    SELECT'U','U',''

    SELECT *

    FROM #mytable

    DECLARE @TempTable TABLE (curItem char(2), prevItem char(2), Trnd int);

    DECLARE @curItem char(2), @prevItem char(2)

    INSERT INTO @TempTable(curItem, prevItem, Trnd)

    SELECTcurItem, prevItem, Trnd

    FROM #mytable

    BEGIN

    SELECT@curItem=curItem,

    @prevItem=prevItem

    FROM @TempTable

    IF @curItem = @prevItem

    BEGIN

    UPDATE EURUSD#1A SET Trnd = 1;

    END

    ELSE IF @curItem <> @prevItem

    BEGIN

    UPDATE EURUSD#1A SET Trnd = 2;

    END

    ELSE

    BEGIN

    UPDATE EURUSD#1A SET Trnd = 3;

    END

    SELECT *

    FROM @TempTable

    END

  • It seems you have possibly over simplified your example - there is no way to ORDER the rows to give you the correct values.

    As the solution will depend a lot on the actual situation you are trying to work in, perhaps you could create a sample that more accurately reflects you r real situation?

    Otherwise we can only give you a general answer that will almost certainly not suit you.

    MM



    select geometry::STGeomFromWKB(0x0106000000020000000103000000010000000B0000001000000000000840000000000000003DD8CCCCCCCCCC0840000000000000003DD8CCCCCCCCCC08408014AE47E17AFC3F040000000000104000CDCCCCCCCCEC3F9C999999999913408014AE47E17AFC3F9C99999999991340000000000000003D0000000000001440000000000000003D000000000000144000000000000000400400000000001040000000000000F03F100000000000084000000000000000401000000000000840000000000000003D0103000000010000000B000000000000000000143D000000000000003D009E99999999B93F000000000000003D009E99999999B93F8014AE47E17AFC3F400000000000F03F00CDCCCCCCCCEC3FA06666666666FE3F8014AE47E17AFC3FA06666666666FE3F000000000000003D1800000000000040000000000000003D18000000000000400000000000000040400000000000F03F000000000000F03F000000000000143D0000000000000040000000000000143D000000000000003D, 0);

  • Forum Etiquette: How to post Reporting Services problems
  • [/url]
  • Forum Etiquette: How to post data/code on a forum to get the best help - by Jeff Moden
  • [/url]
  • How to Post Performance Problems - by Gail Shaw
  • [/url]

  • Sorry,

    This would have made it much easier to understand. The resulting report should look like:

    curItem / prevItem / Trnd

    D U 1

    D U 1

    U D 1

    U U 1

    U U 2

    U U 3

    U U 4

  • Well, no that doesn't help in this case.

    I understand the required result, but the sample data is lacking anything to ORDER by to get that result.

    You cannot rely on the rows being SELECTED in any particular ORDER without specifying the column or expression to ORDER by.

    If you had a row identifier, for instance, we could use that.

    Without it, who's to say the rows won't be SELECTED in this order:

    curItem / prevItem / Trnd

    U U 1

    U U 2

    D U 1

    D U 1

    U D 1

    U U 1

    U U 2

    MM



    select geometry::STGeomFromWKB(0x0106000000020000000103000000010000000B0000001000000000000840000000000000003DD8CCCCCCCCCC0840000000000000003DD8CCCCCCCCCC08408014AE47E17AFC3F040000000000104000CDCCCCCCCCEC3F9C999999999913408014AE47E17AFC3F9C99999999991340000000000000003D0000000000001440000000000000003D000000000000144000000000000000400400000000001040000000000000F03F100000000000084000000000000000401000000000000840000000000000003D0103000000010000000B000000000000000000143D000000000000003D009E99999999B93F000000000000003D009E99999999B93F8014AE47E17AFC3F400000000000F03F00CDCCCCCCCCEC3FA06666666666FE3F8014AE47E17AFC3FA06666666666FE3F000000000000003D1800000000000040000000000000003D18000000000000400000000000000040400000000000F03F000000000000F03F000000000000143D0000000000000040000000000000143D000000000000003D, 0);

  • Forum Etiquette: How to post Reporting Services problems
  • [/url]
  • Forum Etiquette: How to post data/code on a forum to get the best help - by Jeff Moden
  • [/url]
  • How to Post Performance Problems - by Gail Shaw
  • [/url]

  • There are a number of techniques to approach running totals in T-SQL, the most documented of which is the quirky update which is an article and long discussion afterwards by Jeff Moden. You'll find it around the site if you want to.

    However, I agree with Magoo above. You can't do a running total without having a sort control. Now, there are a number of other steps you'll want to include to make sure that things won't go awry when you're not looking, but that's absolutely required.

    If you can't ORDER BY the query to get you the sorting you want, there's just not enough to work with for a Running Totals format with any kind of repeatability.


    - Craig Farrell

    Never stop learning, even if it hurts. Ego bruises are practically mandatory as you learn unless you've never risked enough to make a mistake.

    For better assistance in answering your questions[/url] | Forum Netiquette
    For index/tuning help, follow these directions.[/url] |Tally Tables[/url]

    Twitter: @AnyWayDBA

  • Here is a portion of the complete result set. I need the program to look at the curItem as compared to the prevItem and if there is a match, update "Trnd" column to counter = counter + 1. If there is not a match just apply counter which is equal to 1. For every record read, the counter needs to increment if there is a match. When there is not a match the counter needs to go back to 1. Though I know the structural elements needed, I do not know the best programming construct to apply it to. I explored CASE and IF, ELSE but do not know how to program it. I hope this makes more sense.

    ConcatYearMonthDayHourMinuteSecondMilisecond curRN prevRN curBidprevBidTrndcurItemprevItem

    201301012202021572013010122020215711101.320461.320411UU

    201301012202150282013010122021502812111.320461.320461FU

    201301012203016732013010122030167313121.320461.320461FF

    201301012203017322013010122030173214131.320191.320461DF

    201301012203017922013010122030179215141.320251.320191UD

    201301012203020922013010122030209216151.320411.320251UU

    201301012203023972013010122030239717161.320461.320412UU

    201301012203072332013010122030723318171.320461.320461FU

    201301012203142622013010122031426219181.320461.320461FF

    201301012203145822013010122031458220191.320461.32046 2FF

    201301012203146422013010122031464221201.320161.320461DF

    201301012203152782013010122031527822211.320211.320161UD

    201301012203190632013010122031906323221.320091.320211DU

    201301012203191222013010122031912224231.320051.320091DD

  • mcertini (6/21/2013)


    Here is a portion of the complete result set. I need the program to look at the curItem as compared to the prevItem and if there is a match, update "Trnd" column to counter = counter + 1. If there is not a match just apply counter which is equal to 1. For every record read, the counter needs to increment if there is a match. When there is not a match the counter needs to go back to 1. Though I know the structural elements needed, I do not know the best programming construct to apply it to. I explored CASE and IF, ELSE but do not know how to program it. I hope this makes more sense.

    What you want isn't a "running total", it is simply a 1-n counter, partitioned by a particular set of fields and perhaps ordered by another set of fields. Investigate the ROW_NUMBER() TSQL construct, which will do exactly what is needed (assuming I am reading correctly). BOL has examples, and you can find many online.

    Best,
    Kevin G. Boles
    SQL Server Consultant
    SQL MVP 2007-2012
    TheSQLGuru on googles mail service

  • Thanks SQLGuru for pointing me in the right direction. Taking your direction I worked on Row_Number and Partition By. This gets me ever so close to what I need but as the old saying goes, "So close but yet so far away". Do you know how to get this to start the sequential numbering over again after an iteration sequence? What I noted is that Row_Number continues to iterate over the complete population. I need it to reset after the first continuous records. See below:

    USE

    EURUSD

    GO

    --===== If the test table already exists, drop it

    IF OBJECT_ID('TempDB..#mytable','U') IS NOT NULL

    DROP TABLE #mytable

    --===== Create the test table with

    CREATE TABLE #mytable

    (

    Yearvarchar(4),

    Monthvarchar(2),

    Dayvarchar(2),

    Hourvarchar(2),

    Minutevarchar(2),

    Secondvarchar(2),

    Milisecondvarchar(3),

    curItem char(2)

    )

    INSERT INTO #mytable

    (Year, Month, Day, Hour, Minute, Second, Milisecond, curItem)

    SELECT'2013','05','04','10','12','45','200','F' UNION ALL

    SELECT'2013','05','04','10','12','46','400','D'UNION ALL

    SELECT'2013','05','04','10','12','46','500','U'UNION ALL

    SELECT'2013','05','04','10','12','47','400','U'UNION ALL

    SELECT'2013','05','04','10','12','49','400','U'UNION ALL

    SELECT'2013','05','04','10','12','50','400','D'UNION ALL

    SELECT'2013','05','04','10','12','50','400','D'UNION ALL

    SELECT'2013','05','04','10','12','50','400','F'

    SELECT Year, Month, Day, Hour, Minute, Second, Milisecond, curItem, ROW_NUMBER() OVER (PARTITION BY curItem ORDER BY Year, Month, Day, Hour, Minute, Second, Milisecond, curItem)

    FROM #mytable

    ORDER BY Year, Month, Day, Hour, Minute, Second, Milisecond, curItem

    YearMonthDayHourMinuteSecondMilisecond curItem(No column name)

    20130504101245200F 1

    20130504101246400D 1

    20130504101246500U 1

    20130504101247400U 2

    20130504101249400U 3

    20130504101250400D 2 -> This number needs to be 1

    20130504101250400D 3 -> This number needs to be 2

    20130504101250400F 2 -> This number needs to be 1

  • I think it's crazy that someone split the date/time up like they did. If you want that same kind of output, then get rid of the first CTE in the following and replace all occurances of "DT" with the list of date/time columns you have. Using multiple DATEADDs would probably also make the code faster. I just didn't want to clutter the example with all of that.

    Using your latest test data, the following will do as you wish.

    WITH

    cteSimplifyDateTime AS

    (

    SELECT DT = CAST(Year+Month+Day+' '+ Hour+':'+Minute+':'+Second+'.'+Milisecond AS DATETIME)

    , curItem

    FROM #MyTable

    ),

    cteEnumerateGroups AS

    (

    SELECT DT

    , curItem

    , MyGroup = ROW_NUMBER() OVER (ORDER BY DT,curItem)

    - ROW_NUMBER() OVER (PARTITION BY CurItem ORDER BY DT,curItem)

    FROM cteSimplifyDateTime

    )

    SELECT DT

    , curItem

    , ConsecutiveCount = ROW_NUMBER() OVER (PARTITION BY MyGroup ORDER BY DT,curItem)

    FROM cteEnumerateGroups

    ORDER BY DT,curItem,ConsecutiveCount

    ;

    Here's the output I get from that.

    DT curItem ConsecutiveCount

    ----------------------- ------- --------------------

    2013-05-04 10:12:01.200 F 1

    2013-05-04 10:12:46.400 D 1

    2013-05-04 10:12:46.500 U 1

    2013-05-04 10:12:47.400 U 2

    2013-05-04 10:12:49.400 U 3

    2013-05-04 10:12:50.400 D 1

    2013-05-04 10:12:50.400 D 2

    2013-05-04 10:12:50.400 F 1

    (8 row(s) affected)

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • Jeff, his first post with actual data in it had a "Concat" field, which seems to be a bit numeric with all the date-time values strung together. I think (if it exists already) that field could be used as the order by for the ROW_NUMBER() I was intending for him to use.

    I concur that splitting date-time parts like this almost never brings any benefit...

    Best,
    Kevin G. Boles
    SQL Server Consultant
    SQL MVP 2007-2012
    TheSQLGuru on googles mail service

  • Jeff & Kevin,

    Thank you for your input, it is much appreciated. As I am a rookie in SQL programming, the included code is so beneficial to learning what is possible.

    I took the most recent recommendation and applied it against my database. There appears to be some discrepancies though. Here is what I am seeing. Listed below in the code is the data for what is reflected here.

    DT curItem ConsecutiveCount

    2013-01-02 02:07:14.480U 2

    2013-01-02 02:07:14.540U 3

    2013-01-02 02:07:14.720D 1

    2013-01-02 02:07:15.140D 2

    2013-01-02 02:07:15.200F 2 -> Should be 1

    2013-01-02 02:07:15.557D 1

    2013-01-02 02:07:16.110U 1

    2013-01-02 02:07:16.530F 1

    2013-01-02 02:07:16.953D 1

    2013-01-02 02:07:17.020F 2 -> Should be 1

    2013-01-02 02:07:17.557F 3 -> Should be 2

    2013-01-02 02:07:17.977U 1

    2013-01-02 02:07:18.540F 3 -> Should be 1

    2013-01-02 02:07:19.047F 4 -> Should be 2

    2013-01-02 02:07:19.423D 1

    2013-01-02 02:07:19.547F 2 -> Should be 1

    2013-01-02 02:07:19.910U 1

    2013-01-02 02:07:20.210F 2

    2013-01-02 02:07:20.270D 1

    2013-01-02 02:07:20.807U 1

    2013-01-02 02:07:21.310F 2 ->Should be 1

    2013-01-02 02:07:22.470F 3 ->Should be 2

    --===== If the test table already exists, drop it

    IF OBJECT_ID('TempDB..#mytable','U') IS NOT NULL

    DROP TABLE #mytable

    --===== Create the test table with

    CREATE TABLE #mytable

    (

    Yearvarchar(4),

    Monthvarchar(2),

    Dayvarchar(2),

    Hourvarchar(2),

    Minutevarchar(2),

    Secondvarchar(2),

    Milisecondvarchar(3),

    curItem char(2)

    )

    INSERT INTO #mytable

    (Year, Month, Day, Hour, Minute, Second, Milisecond, curItem)

    SELECT'2013','01','02','02','07','14','480', 'U' UNION ALL

    SELECT'2013','01','02','02','07','14','540', 'U' UNION ALL

    SELECT'2013','01','02','02','07','14','720', 'D' UNION ALL

    SELECT'2013','01','02','02','07','15','140', 'D' UNION ALL

    SELECT'2013','01','02','02','07','15','200', 'F' UNION ALL

    SELECT'2013','01','02','02','07','15','557', 'D' UNION ALL

    SELECT'2013','01','02','02','07','16','110', 'U' UNION ALL

    SELECT'2013','01','02','02','07','16','530', 'F' UNION ALL

    SELECT'2013','01','02','02','07','16','953', 'D' UNION ALL

    SELECT'2013','01','02','02','07','17','020', 'F' UNION ALL

    SELECT'2013','01','02','02','07','17','557', 'F' UNION ALL

    SELECT'2013','01','02','02','07','17','977', 'U' UNION ALL

    SELECT'2013','01','02','02','07','18','540', 'F' UNION ALL

    SELECT'2013','01','02','02','07','19','047', 'F' UNION ALL

    SELECT'2013','01','02','02','07','19','423', 'D' UNION ALL

    SELECT'2013','01','02','02','07','19','547', 'F' UNION ALL

    SELECT'2013','01','02','02','07','19','910', 'U' UNION ALL

    SELECT'2013','01','02','02','07','20','210', 'F' UNION ALL

    SELECT'2013','01','02','02','07','20','270', 'D' UNION ALL

    SELECT'2013','01','02','02','07','20','807', 'U' UNION ALL

    SELECT'2013','01','02','02','07','21','310', 'F' UNION ALL

    SELECT'2013','01','02','02','07','22','470', 'F'

    ;WITH

    cteSimplifyDateTime AS

    (

    SELECT DT = CAST(Year+Month+Day+' '+Hour+':'+Minute+':'+Second+'.'+Milisecond AS DATETIME), curItem

    FROM #mytable

    ),

    cteEnumerateGroups AS

    (

    SELECT DT, curItem, MyGroup = ROW_NUMBER() OVER (ORDER BY DT, curItem)-

    ROW_NUMBER() OVER (PARTITION BY curItem ORDER BY DT, curItem)

    FROM cteSimplifyDateTime

    )

    SELECT DT, curItem, ConsecutiveCount = ROW_NUMBER() OVER (PARTITION BY MyGroup ORDER BY DT, curItem)

    FROM cteEnumerateGroups

    ORDER BY DT, curItem, ConsecutiveCount

  • Does this do the trick?

    ;WITH

    cteSimplifyDateTime AS

    (

    SELECT DT = CAST(Year+Month+Day+' '+Hour+':'+Minute+':'+Second+'.'+Milisecond AS DATETIME), curItem

    FROM #mytable

    ),

    cteEnumerateGroups AS

    (

    SELECT DT, curItem, MyGroup = ROW_NUMBER() OVER (ORDER BY DT, curItem)-

    ROW_NUMBER() OVER (PARTITION BY curItem ORDER BY DT, curItem)

    FROM cteSimplifyDateTime

    )

    SELECT DT, curItem, ConsecutiveCount = ROW_NUMBER() OVER (PARTITION BY curItem, MyGroup ORDER BY MyGroup, DT, curItem)

    FROM cteEnumerateGroups

    ORDER BY DT, curItem, ConsecutiveCount

    Best,
    Kevin G. Boles
    SQL Server Consultant
    SQL MVP 2007-2012
    TheSQLGuru on googles mail service

  • Kevin,

    Awsome! Thank you for the solution.

    Mike

  • mcertini (6/22/2013)


    Kevin,

    Awsome! Thank you for the solution.

    Mike

    Glad I could help! I got to stand on the shoulders of a TSQL Giant (hey Jeff!)! 🙂

    BTW, please do use the numeric field from your first data-containing post if it exists. No sense in paying the cost to split and then recombine all those fields.

    Best,
    Kevin G. Boles
    SQL Server Consultant
    SQL MVP 2007-2012
    TheSQLGuru on googles mail service

  • TheSQLGuru (6/23/2013)


    I got to stand on the shoulders of a TSQL Giant (hey Jeff!)! 🙂

    So that's what it was... I had a giant standing on my shoulders. 🙂

    No pun intended but I had that sinking feeling that I was forgetting something. I didn't have the time to gen a wad o' test data like I normally do, though. Thank you VERY much for the cover, Kevin.

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • Viewing 15 posts - 1 through 15 (of 16 total)

    You must be logged in to reply to this topic. Login to reply