Reputation: 7984
I have a table that has monthly statistics
Calendar_Date Month Monthly Stat
1 Jun 16 Jun-16 1011
1 Jul 16 Jul-16 1034
1 Aug 16 Aug-16 1003
1 Sep 16 Sep-16 991
1 Oct 16 Oct-16 988
1 Nov 16 Nov-16 999
1 Dec 16 Dec-16 994
1 Jan 17 Jan-17 981
1 Feb 17 Feb-17 964
1 Mar 17 Mar-17 966
1 Apr 17 Apr-17 948
1 May 17 May-17 942
1 Jun 17 Jun-17 945
1 Jul 17 Jul-17 938
1 Aug 17 Aug-17 942
1 Sep 17 Sep-17 941
1 Oct 17 Oct-17 909
1 Nov 17 Nov-17 911
1 Dec 17 Dec-17 914
1 Jan 18 Jan-18 929
1 Feb 18 Feb-18 913
I want to find the annual statitcs that will be the previous 12 records So the stat for Dec-2017 will be the sum of 12 records before Dec-2017 which are Dec-2017 + Nov-17 + Oct-17 + ... + Jan-17 divided by 12
Calendar_Date Month Monthly Stat Yearly Stat
1 Jun 16 Jun-16 1011 -
1 Jul 16 Jul-16 1034 -
1 Aug 16 Aug-16 1003 -
1 Sep 16 Sep-16 991 -
1 Oct 16 Oct-16 988 -
1 Nov 16 Nov-16 999 -
1 Dec 16 Dec-16 994 -
1 Jan 17 Jan-17 981 -
1 Feb 17 Feb-17 964 -
1 Mar 17 Mar-17 966 -
1 Apr 17 Apr-17 948 -
1 May 17 May-17 942 985.08
1 Jun 17 Jun-17 945 979.58
1 Jul 17 Jul-17 938 971.58
1 Aug 17 Aug-17 942 966.50
1 Sep 17 Sep-17 941 962.33
1 Oct 17 Oct-17 909 955.75
1 Nov 17 Nov-17 911 948.33
1 Dec 17 Dec-17 914 941.75
1 Jan 18 Jan-18 929 937.42
1 Feb 18 Feb-18 913 933.17
I can do that by joining the table to itself 12 times
SELECT *
FROM MyTable A
INNER JOIN MyTable B ON DATEADD(M,-1,a.Calendar_Date) = b.Calendar_Date
INNER JOIN MyTable C ON DATEADD(M,-2,a.Calendar_Date) = c.Calendar_Date
...
INNER JOIN MyTable L ON DATEADD(M,-12,a.Calendar_Date) = l.Calendar_Date
which is not very practicle any better idea how to do that?
Upvotes: 2
Views: 3423
Reputation: 1596
What you want is a windowing function.
SELECT Calendar_Date, Month, MonthlyStat,
IIF(ROW_NUMBER() OVER(ORDER BY Calendar_Date)>=12,
ROUND(AVG(CAST(MonthlyStat AS DECIMAL))
OVER (ORDER BY Calendar_Date ASC ROWS BETWEEN 11 PRECEDING AND CURRENT ROW),2),
NULL) AS YearlyStat
FROM MyTable ORDER BY Calendar_Date
AVG(MonthlyStat) OVER (ORDER BY Calendar_Date ASC ROWS BETWEEN 11 PRECEDING AND CURRENT ROW)
takes the current row, and the 11 rows before it ordered by Calendar_Date, and give you the Average. We CAST
MonthlyStat as decimal so the code doesn't return intergers, then ROUND
to 2 decimal places as this is currency.
If you want for the 12 months before this month your ROWS
clause in your windowing function becomes ROWS BETWEEN 12 PRECEDING AND 1 PRECEDING
IIF(ROW_NUMBER() OVER(ORDER BY Calendar_Date)>=12,[...],NULL)
tests if this is the 12th or later row, and only calculates a yearly average for those rows where data exists for 12 months, returning null for rows 1-11. If you do change the ROWS clause, you also need to change this to skip one more row, just change >=
to >
.
Upvotes: 5
Reputation: 843
You should probably look at window functions to SUM the current and previous number of rows data then divide by 12
Something like:
SELECT
Calendar_Date
, Month
, Monthly_Stat
, (SUM(Monthly_Stat) OVER (ORDER BY Calendar_Date ROWS BETWEEN 11 PRECEDING AND CURRENT ROW)) / 12 AS Yearly_Stat
FROM MyTable
Upvotes: 2
Reputation: 69504
SELECT *
, AVG(MonthlyStat) OVER (ORDER BY CAST(REPLACE(Calendar_Date,' ','') AS DATE) ASC
ROWS BETWEEN 11 PRECEDING AND CURRENT ROW) AS YearlyStat
FROM YourTable
Upvotes: 1