help!!! calculate average rolling 4 last weeks

  • Hi All,

    I need your help.

    Database: SQL SERVER 2008R2

    I want to calculte for a year and a week the average of value of the 4 last weeks.

    I have data a table like that:

    YEAR WEEKS VALUE

    2012 1 3000

    2012 2 5000

    2012 3 6000

    2012 4 7000

    2012 5 8000

    2012 6 9000

    2012 7 1000

    2012 8 6000

    And I want that :

    YEAR WEEKS VALUE

    2012 1 ( Average value for week 49, 50, 51, 52 for the year 2011)

    2012 2 ( Average value for week 50, 51, 52 for the year 2011 and week 1 for the year 2012)

    2012 3 ( Average value for week 51, 52 for the year 2011 and week 1, 2 for the year 2012)

    2012 4 ( Average value for week 52 for the year 2011 and week 1, 2, 3 for the year 2012)

    2012 5 5250 -> ( Average value for week 1, 2, 3 , 4 for the year 2012)

    2012 6 6500 -> ( Average value for week 2, 3 , 4, 5 for the year 2012)

    Thank U for your help

  • Here you go..

    -- Creating table

    CREATE TABLE year_week_value

    (

    year INT,

    week INT,

    value INT

    );

    -- Inserting test values

    INSERT year_week_value

    VALUES

    (2011,49,3000),

    (2011,50,5000),

    (2011,51,6000),

    (2011,52,7000),

    (2012,1,3000),

    (2012,2,5000),

    (2012,3,6000),

    (2012,4,7000),

    (2012,5,8000),

    (2012,6,9000),

    (2012,7,1000),

    (2012,8,6000);

    -- Resultant query

    SELECT a.year,

    a.week,

    .25 * ( Isnull(b.value, 0) + Isnull(c.value, 0)

    + Isnull(d.value, 0) + Isnull(e.value, 0) ) AvgVal

    FROM year_week_value a

    LEFT JOIN year_week_value b

    ON b.year = Year(Dateadd(wk, a.week - 2, Dateadd(year, a.year - 1900, 0)))

    AND b.week = Datepart(wk, Dateadd(wk, a.week - 2, 0))

    LEFT JOIN year_week_value c

    ON c.year = Year(Dateadd(wk, a.week - 3, Dateadd(year, a.year - 1900, 0)))

    AND c.week = Datepart(wk, Dateadd(wk, a.week - 3, 0))

    LEFT JOIN year_week_value d

    ON d.year = Year(Dateadd(wk, a.week - 4, Dateadd(year, a.year - 1900, 0)))

    AND d.week = Datepart(wk, Dateadd(wk, a.week - 4, 0))

    LEFT JOIN year_week_value e

    ON e.year = Year(Dateadd(wk, a.week - 5, Dateadd(year, a.year - 1900, 0)))

    AND e.week = Datepart(wk, Dateadd(wk, a.week - 5, 0))

    ORDER BY a.year,

    a.week

    ~ Lokesh Vij


    Guidelines for quicker answers on T-SQL question[/url]
    Guidelines for answers on Performance questions

    Link to my Blog Post --> www.SQLPathy.com[/url]

    Follow me @Twitter

  • Here is another way using Correlated SubQuery:

    Create Table Ex1

    (

    YEAR int,

    WEEKS int,

    VALUE int

    )

    Insert Into Ex1

    Select 2012, 1,3000

    Union ALL

    Select 2012, 2,5000

    Union ALL

    Select 2012, 3,6000

    Union ALL

    Select 2012, 4,7000

    Union ALL

    Select 2012, 5,8000

    Union ALL

    Select 2012, 6,9000

    Union ALL

    Select 2012, 7,1000

    Union ALL

    Select 2012, 8,6000

    ;With CTE

    As

    (

    Select *, ROW_NUMBER() Over(Order By Year, Weeks) As rn From Ex1

    )

    Select *, (Case When (Select Count(*) From CTE Where rn < a.rn) >= 4 Then (Select AVG(VALUE) From CTE As b Where b.rn IN(Select Top 4 rn From CTE Where rn < a.rn Order By rn DESC)) Else '' End) As AvgValue

    From CTE As a

    Order By YEAR, WEEKS

    Hope it helps.:-)

    Vinu Vijayan

    For better and faster solutions please check..."How to post data/code on a forum to get the best help" - Jeff Moden[/url] 😉

  • Hi Lokesh,

    Thank You for your answer.

    I tested the script but the execution is too long ( about 30 minutes for 1064 lines) :(.

    Do U think that it is possible to optimize the script ?

    Can I replace 'left join' by ' inner join ' ??

    Lidou

  • Hi Vinu,

    thank U for your answer !!!

    I tested the script but I have an error !!! it is in french .

    Msg 116, Niveau 16, État 1, Ligne 8

    Une seule expression peut être spécifiée dans la liste de sélection quand la sous-requête n'est pas introduite par EXISTS.

    So I can Use only one expression in a sub request. 🙁

  • Hi Lokesh,

    that works well with index .

    Thank U !!!!!!!!!

  • I guess I'd do it like this: -

    SELECT year, week, value, ISNULL(AvgVal,0) AS AvgVal

    FROM (SELECT year, week, value, ROW_NUMBER() OVER(ORDER BY year,week)

    FROM year_week_value) a(year,week,value,pos)

    OUTER APPLY (SELECT SUM(b.value)/(COUNT(1)*1.00)

    FROM (SELECT year, week, value, ROW_NUMBER() OVER(ORDER BY year,week)

    FROM year_week_value) b(year,week,value,pos)

    WHERE a.pos >= b.pos AND a.pos-4 <= b.pos) ab(AvgVal);

    SELECT year, week, value, ISNULL(AvgVal,0) AS AvgVal

    FROM (SELECT year, week, value, ROW_NUMBER() OVER(ORDER BY year,week)

    FROM year_week_value) a(year,week,value,pos)

    OUTER APPLY (SELECT SUM(b.value)/(COUNT(1)*1.00)

    FROM (SELECT year, week, value, ROW_NUMBER() OVER(ORDER BY year,week)

    FROM year_week_value) b(year,week,value,pos)

    WHERE a.pos > b.pos AND a.pos-5 < b.pos) ab(AvgVal);

    However, all three of the solutions so far give different results so we may have all misunderstood your requirements.

    So, let's look at some sample data: -

    -- Creating table

    CREATE TABLE year_week_value (year INT, week INT, value INT);

    -- Inserting test values

    INSERT year_week_value

    VALUES (2011, 49, 3000), (2011, 50, 5000), (2011, 51, 6000), (2011, 52, 7000),

    (2012, 1, 3000), (2012, 2, 5000), (2012, 3, 6000), (2012, 4, 7000),

    (2012, 5, 8000), (2012, 6, 9000), (2012, 7, 1000), (2012, 8, 6000);

    Now the three solutions: -

    -- Lokesh Vij

    SELECT a.year, a.week, .25 * (Isnull(b.value, 0) + Isnull(c.value, 0) + Isnull(d.value, 0) + Isnull(e.value, 0)) AvgVal, A.value

    FROM year_week_value a

    LEFT JOIN year_week_value b ON b.year = Year(Dateadd(wk, a.week - 2, Dateadd(year, a.year - 1900, 0)))

    AND b.week = Datepart(wk, Dateadd(wk, a.week - 2, 0))

    LEFT JOIN year_week_value c ON c.year = Year(Dateadd(wk, a.week - 3, Dateadd(year, a.year - 1900, 0)))

    AND c.week = Datepart(wk, Dateadd(wk, a.week - 3, 0))

    LEFT JOIN year_week_value d ON d.year = Year(Dateadd(wk, a.week - 4, Dateadd(year, a.year - 1900, 0)))

    AND d.week = Datepart(wk, Dateadd(wk, a.week - 4, 0))

    LEFT JOIN year_week_value e ON e.year = Year(Dateadd(wk, a.week - 5, Dateadd(year, a.year - 1900, 0)))

    AND e.week = Datepart(wk, Dateadd(wk, a.week - 5, 0))

    ORDER BY a.year, a.week;

    -- vinu512

    WITH CTE

    AS (SELECT *, ROW_NUMBER() OVER (ORDER BY year, week) AS rn

    FROM year_week_value

    )

    SELECT *, (CASE WHEN (SELECT Count(*)

    FROM CTE

    WHERE rn < a.rn) >= 4

    THEN (SELECT AVG(VALUE)

    FROM CTE AS b

    WHERE b.rn IN (SELECT TOP 4 rn

    FROM CTE

    WHERE rn < a.rn

    ORDER BY rn DESC

    ))

    ELSE ''

    END) AS AvgValue

    FROM CTE AS a

    ORDER BY year, week;

    -- Cadavre

    SELECT year, week, value, ISNULL(AvgVal,0) AS AvgVal

    FROM (SELECT year, week, value, ROW_NUMBER() OVER(ORDER BY year,week)

    FROM year_week_value) a(year,week,value,pos)

    OUTER APPLY (SELECT SUM(b.value)/(COUNT(1)*1.00)

    FROM (SELECT year, week, value, ROW_NUMBER() OVER(ORDER BY year,week)

    FROM year_week_value) b(year,week,value,pos)

    WHERE a.pos >= b.pos AND a.pos-4 <= b.pos) ab(AvgVal);

    And the results: -

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

    Lokesh Vij

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

    year week AvgVal value

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

    2011 49 0.00 3000

    2011 50 750.00 5000

    2011 51 2000.00 6000

    2011 52 3500.00 7000

    2012 1 5250.00 3000

    2012 2 5250.00 5000

    2012 3 5250.00 6000

    2012 4 5250.00 7000

    2012 5 5250.00 8000

    2012 6 6500.00 9000

    2012 7 7500.00 1000

    2012 8 6250.00 6000

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

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

    vinu512

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

    year week value rn AvgValue

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

    2011 49 3000 1 0

    2011 50 5000 2 0

    2011 51 6000 3 0

    2011 52 7000 4 0

    2012 1 3000 5 5250

    2012 2 5000 6 5250

    2012 3 6000 7 5250

    2012 4 7000 8 5250

    2012 5 8000 9 5250

    2012 6 9000 10 6500

    2012 7 1000 11 7500

    2012 8 6000 12 6250

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

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

    Cadavre

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

    year week value AvgVal

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

    2011 49 3000 0.000000000000000

    2011 50 5000 3000.000000000000000

    2011 51 6000 4000.000000000000000

    2011 52 7000 4666.666666666666666

    2012 1 3000 5250.000000000000000

    2012 2 5000 5250.000000000000000

    2012 3 6000 5250.000000000000000

    2012 4 7000 5250.000000000000000

    2012 5 8000 5250.000000000000000

    2012 6 9000 6500.000000000000000

    2012 7 1000 7500.000000000000000

    2012 8 6000 6250.000000000000000

    So I guess my advice is to always post sample data and expected results in the future. But for this problem, you need to figure out whether any of these solutions give the results you expect.


    Forever trying to learn
    My blog - http://www.cadavre.co.uk/
    For better, quicker answers on T-SQL questions, click on the following...http://www.sqlservercentral.com/articles/Best+Practices/61537/
    For better, quicker answers on SQL Server performance related questions, click on the following...http://www.sqlservercentral.com/articles/SQLServerCentral/66909/

  • Lidou123 (12/20/2012)


    Hi Vinu,

    thank U for your answer !!!

    I tested the script but I have an error !!! it is in french .

    Msg 116, Niveau 16, État 1, Ligne 8

    Une seule expression peut être spécifiée dans la liste de sélection quand la sous-requête n'est pas introduite par EXISTS.

    So I can Use only one expression in a sub request. 🙁

    Hi Lidou,

    Are you executing the same script that I posted??.....It seems to work perfectly on my box and I think on Cadavre's box as well.

    Vinu Vijayan

    For better and faster solutions please check..."How to post data/code on a forum to get the best help" - Jeff Moden[/url] 😉

  • Lidou123 (12/20/2012)


    Hi Lokesh,

    that works well with index .

    Thank U !!!!!!!!!

    Gald that you found out a way to improve the query using an Index 🙂

    ~ Lokesh Vij


    Guidelines for quicker answers on T-SQL question[/url]
    Guidelines for answers on Performance questions

    Link to my Blog Post --> www.SQLPathy.com[/url]

    Follow me @Twitter

  • Cadavre (12/20/2012)


    So I guess my advice is to always post sample data and expected results in the future. But for this problem, you need to figure out whether any of these solutions give the results you expect.

    I take by your words Cadavre!

    OP - please take care in future. That will really help you and us both 🙂

    ~ Lokesh Vij


    Guidelines for quicker answers on T-SQL question[/url]
    Guidelines for answers on Performance questions

    Link to my Blog Post --> www.SQLPathy.com[/url]

    Follow me @Twitter

  • vinu512 (12/20/2012)


    Are you executing the same script that I posted??.....It seems to work perfectly on my box and I think on Cadavre's box as well.

    I think your script is fine!

    ~ Lokesh Vij


    Guidelines for quicker answers on T-SQL question[/url]
    Guidelines for answers on Performance questions

    Link to my Blog Post --> www.SQLPathy.com[/url]

    Follow me @Twitter

  • Thank U guys.

    just let me test the 3 scripts and I will come back !!!!!!!!

    Thank U

    Lidou

  • Lidou123 (12/19/2012)


    Hi All,

    YEAR WEEKS VALUE

    2012 1 3000

    2012 2 5000

    2012 3 6000

    2012 4 7000

    2012 5 8000

    2012 6 9000

    2012 7 1000

    2012 8 6000

    And I want that :

    YEAR WEEKS VALUE

    2012 1 ( Average value for week 49, 50, 51, 52 for the year 2011)

    2012 2 ( Average value for week 50, 51, 52 for the year 2011 and week 1 for the year 2012)

    2012 3 ( Average value for week 51, 52 for the year 2011 and week 1, 2 for the year 2012)

    2012 4 ( Average value for week 52 for the year 2011 and week 1, 2, 3 for the year 2012)

    2012 5 5250 -> ( Average value for week 1, 2, 3 , 4 for the year 2012)

    2012 6 6500 -> ( Average value for week 2, 3 , 4, 5 for the year 2012)

    Thank U for your help

    Guys. I'm not sure I'm reading the OP's problem quite the same way you are.

    He seems to want the average to represent the 4 weeks prior to but not including the current week. So if there's data for weeks 48-52, the first week to show is week 1. At the same time, since there's data for up through week 8, that means the value for week 9 should show.

    Something like this perhaps?

    -- Creating table

    CREATE TABLE #year_week_value

    (

    year INT,

    week INT,

    value INT

    );

    -- Inserting test values

    INSERT #year_week_value (year, week, value)

    VALUES

    (2011,49,3000),

    (2011,50,5000),

    (2011,51,6000),

    (2011,52,7000),

    (2012,1,3000),

    (2012,2,5000),

    (2012,3,6000),

    (2012,4,7000),

    (2012,5,8000),

    (2012,6,9000),

    (2012,7,1000),

    (2012,8,6000);

    -- Dwain.C

    SELECT year=MAX(yr_wk/100+CASE WHEN yr_wk%100=52 THEN 1 ELSE 0 END)

    ,week=MAX(CASE WHEN yr_wk%100=52 THEN 1 ELSE 1+(yr_wk%100) END)

    ,avgvalue=SUM((value + value2 + value3 + value4) / 4)

    FROM (

    SELECT yr_wk=year*100+week

    ,value, value2=0, value3=0, value4=0

    FROM #year_week_value

    UNION ALL

    SELECT CASE WHEN week + 1 > 52 THEN (year+1)*100 + (week + 1)-52 ELSE year*100 + week + 1 END

    , 0, value2=value, value3=0, value4=0

    FROM #year_week_value

    UNION ALL

    SELECT CASE WHEN week + 2 > 52 THEN (year+1)*100 + (week + 2)-52 ELSE year*100 + week + 2 END

    , 0, value2=0, value3=value, value4=0

    FROM #year_week_value

    UNION ALL

    SELECT CASE WHEN week + 3 > 52 THEN (year+1)*100 + (week + 3)-52 ELSE year*100 + week + 3 END

    , 0, value2=0, value3=0, value4=value

    FROM #year_week_value) a

    GROUP BY yr_wk

    HAVING COUNT(yr_wk) = 4

    DROP TABLE #year_week_value


    My mantra: No loops! No CURSORs! No RBAR! Hoo-uh![/I]

    My thought question: Have you ever been told that your query runs too fast?

    My advice:
    INDEXing a poor-performing query is like putting sugar on cat food. Yeah, it probably tastes better but are you sure you want to eat it?
    The path of least resistance can be a slippery slope. Take care that fixing your fixes of fixes doesn't snowball and end up costing you more than fixing the root cause would have in the first place.

    Need to UNPIVOT? Why not CROSS APPLY VALUES instead?[/url]
    Since random numbers are too important to be left to chance, let's generate some![/url]
    Learn to understand recursive CTEs by example.[/url]
    [url url=http://www.sqlservercentral.com/articles/St

Viewing 13 posts - 1 through 12 (of 12 total)

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