Question

I'm wondering if either a running total or total-by-time-block for sales data can be generated using only SQL.

Let's say I have a simple table that records sales and the time they occurred.

ID | Timestamp           | Amount
1  | 2014-03-04 09:00:00 | 25.00
2  | 2014-03-04 09:02:25 | 15.00
3  | 2014-03-04 09:13:00 |  5.00
4  | 2014-03-04 09:16:11 | 17.50
5  | 2014-03-04 09:28:18 | 44.50
...

I can easily calculate the total sales for a day with a query like:

SELECT sum(Amount) from Sales
WHERE Timestamp BETWEEN '2014-03-04 00:00:00' AND '2014-03-04 23:59:59'

But I'd like to all calculate the amounts sold during each (say) 15 minute period to get a result like:

08:45 |  0.00
09:00 | 45.00
09:15 | 62:00 
...

and a cumulative running total for each (say) 15 minute period to produce a result like:

08:45 |   0:00
09:00 |  40.00
09:15 | 107:00 
...

I can write a simple program or use a spreadsheet to achieve these two results given the raw data, but I'm wondering how to do it just using SQL. Is it possible? If so, how?

EDIT: If possible, a DB-agnostic solution would be preferred. I use SQL Server at present.

No correct solution

OTHER TIPS

In SQL Server 2012, you can do this using the cumulative sum window function. You can also get the timeslot in a way that comes close to working in more than one database:

select timeslot,
       sum(amount) as amount,
       sum(sum(amount)) over (order by timeslot) as cumamount
from (select t.*,
             (cast('2014-03-04 00:00:00' as datetime) +
              cast( ("timestamp" - cast('2014-03-04 00:00:00' as datetime))*24*4 as int)/(24.0*4)
             ) as timeslot
      from table t
     ) t
where Timestamp between '2014-03-04 00:00:00' and '2014-03-04 23:59:59'
group by timeslot;

The idea behind the timeslot calculation is to take the difference between timestamp and midnight of some day. This gives the number of days (with fractions) between the two dates. Then multiply this by 24 for hours and 4 for the 15-minute intervals, and it gives the number of 15 minute intervals since midnight on some date. Truncate this value by converting to an integer and add back to the original date. This is all done in a subquery, so the calculation can be repeated.

This approach will work in many databases, though there might be some nuances on the exact expression. The formatting of the datetime would be rather database specific.

The rest is just using the cumulative sum function. If you don't have this, then you can use a correlated subquery instead.

I do not have sol for first request. (total for every "Timeslot" kind of query) but I do have sol for second request. (cumulative running total for each "Timeslot")

AS Gordon mentioned with SQL server 2012 this is much simpler. yet as I am providing an old way which can be done on SQL 2005 onward.

Also solution is not 100% database agnostic, but easier to translate from SQL-SERVER to ORACLE or DB2 or anything else.

before going to actual query check out the functions i created to simply give me a TimeSlot values when I give two Date Range. UFN to GET TIMESLOT Values

Note that the function is created at different granularity level by Slot Type. Hour, Minute, Second etc.... you can create new as you like.

In the below sample query I am choosing the timeslot of 11-Seconds.

check the result here. Sample Output

    DECLARE @dt TABLE
    (
        RowID               INT IDENTITY    NOT NULL
        ,LastModified       DATETIME2(2)    NOT NULL
        ,Amount             INT NOT NULL DEFAULT 0
    )

    INSERT INTO @dt( LastModified, Amount )
                SELECT '2014-03-04 00:00:00.00', 10
    UNION ALL   SELECT '2014-03-04 00:00:05.00', 10
    UNION ALL   SELECT '2014-03-04 00:00:10.00', 10
    UNION ALL   SELECT '2014-03-04 00:00:15.00', 10
    UNION ALL   SELECT '2014-03-04 00:00:20.00', 10
    UNION ALL   SELECT '2014-03-04 00:00:25.00', 10
    UNION ALL   SELECT '2014-03-04 00:00:30.00', 10
    UNION ALL   SELECT '2014-03-04 00:00:35.00', 10
    UNION ALL   SELECT '2014-03-04 00:00:40.00', 10
    UNION ALL   SELECT '2014-03-04 00:00:45.00', 10
    UNION ALL   SELECT '2014-03-04 00:00:50.00', 10


    DECLARE @DatePart sysname
            ,@SlotValue INT
            ,@MinDt DATETIME2(2)
            ,@MaxDt DATETIME2(2)

    SET @SlotValue = 11
    SELECT  @MinDt=MIN(LastModified)
            ,@MaxDt=MAX(LastModified)
    FROM @dt

    ;WITH AllDt(RowID,timeslot,amount)
    AS
    (
        SELECT  CAST (ROW_NUMBER() OVER (ORDER BY COALESCE(t1.TimeSlot,t2.LastModified)) AS INT) RowID
                ,COALESCE(t1.TimeSlot,t2.LastModified)
                ,ISNULL(t2.Amount,0) AS Amount
        FROM dbo.ufn_utl_timeslotBySecond(@SlotValue,@MinDt,@MaxDt) t1
        FULL OUTER JOIN @dt t2
            ON t1.TimeSlot=t2.LastModified
    )
    ,
    RCTE1(RowID,timeslot,amount)
    AS
    (
        SELECT  RowID 
                ,timeslot
                ,Amount
        FROM AllDt
        WHERE RowID=1
        UNION ALL

        SELECT dt.RowID,dt.TimeSlot,CAST(dt.Amount+t3.amount AS INT) AS amount
        FROM ALLDt dt
        JOIN RCTE1 t3
            ON dt.RowID=t3.RowID+1
    )
    SELECT * 
    FROM RCTE1 
    ORDER BY TimeSlot
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top