Search code examples
sql-serversql-server-2014rowsbetweenmoving-average

6 weeks Moving Average Over Same Day of week


I am calculating the 6 weeks moving average, for the same days of the week, for the volume of calls in a call center.

What I mean by this is the same 6 previous days (last 6 tuesdays, last 6 wednesdays, etc.)

I've got the following code working, but not flexible at all:

SELECT
    [ROW_DATE],
    [DEPARTMENT_DESC],
    [totalcalls],
    AVG([TOTALCALLS]) OVER(ORDER BY [DEPARTMENT_DESC],
                                    [ROW_DATE] ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS [MOVING_AVG]
FROM
(
    SELECT
        [ROW_DATE] AS [ROW_DATE],
        [DEPARTMENT_DESC] AS [DEPARTMENT_DESC],
        SUM([CALLS_OFFERED_ACTUALS]) AS [TOTALCALLS]
    FROM [GEMDB].[dbo].[V_PRD_ACT_HSPLIT_intradayLCWcallsTable]
    WHERE ROW_DATE IN(CONVERT(DATE, GETDATE() - 42), CONVERT(DATE, GETDATE() - 7), CONVERT(DATE, GETDATE() - 14), CONVERT(DATE, GETDATE() - 21), CONVERT(DATE, GETDATE() - 28), CONVERT(DATE, GETDATE() - 35))
    AND [DEPARTMENT_DESC] = 'techops'
    GROUP BY
        ROW_DATE,
        [DEPARTMENT_DESC]
) AS tbl
ORDER BY
    ROW_DATE,
    [DEPARTMENT_DESC];

The output looks like this: Moving Average for August 23rd

The issue with the above code is it gives me the value for 1 day of the week (August 23rd). I would like to get the 6 weeks moving average for the last 7 days, without writing GETDATE 49 times, which would be crazy.

Any help would be appreciated.


Solution

  • I've tried to create a simplified example of what you are asking if I've understood it correctly. This uses 2 weeks worth of data but should work for larger datasets. You can run this code in isolation to test and adapt if it fits your requirement:

    CREATE TABLE #calls
    (
        TotalCalls INT,
        CallDate DATE
    );
    
    -- 2 weeks worth of calls
    INSERT INTO #calls
    (
        TotalCalls,
        CallDate
    )
    VALUES
    (12, '20170801'), -- starts on a Tuesday
    (13, '20170802'),
    (17, '20170803'),
    (20, '20170804'),
    (4, '20170805'),
    (8, '20170806'),
    (10, '20170807'),
    (14, '20170808'),
    (18, '20170809'),
    (16, '20170810'),
    (7, '20170811'),
    (11, '20170812'),
    (19, '20170813'),
    (14, '20170814');
    
    -- casts to numeric with decimal places for the average for accuracy
    -- DayName - gives the day names for grouping
    -- DayNo - gives you the day number for ordering
    -- Instances - gives you how many days were included in the average
    -- WHERE - filters to dates >= date - 42 for 6 weeks
    SELECT AVG(CAST(c.TotalCalls AS NUMERIC(5,2))) AvgCalls,
           DATENAME(dw,c.CallDate ) DayName,
           DATEPART(dw, c.CallDate) DayNo,
           COUNT(c.CallDate) Instances
    FROM #calls AS c
    WHERE c.CallDate > GETDATE() - 42
    GROUP BY DATENAME(dw, c.CallDate ), DATEPART(dw, c.CallDate)
    ORDER BY DATEPART(dw, c.CallDate)
    

    Produces:

    AvgCalls    DayName    DayNo    Instances
    13.500000   Sunday     1        2
    12.000000   Monday     2        2
    13.000000   Tuesday    3        2
    15.500000   Wednesday  4        2
    16.500000   Thursday   5        2
    13.500000   Friday     6        2
    7.500000    Saturday   7        2