计算滚动小计的方法

时间:2013-08-01 20:13:10

标签: sql

说我有一个像这样结构的表:

id     dt        val
a     1/1/2012   23
a     2/1/2012   24
a     6/1/2013   12
a     7/1/2013   56
b     1/1/2009   34
b     3/1/2009   78

每个id都有一个月份dt和一个值。可能有几个月缺失,但永远不会有重复的月份。

我需要计算每个数据点的12个月滚动平均值。例如,第四行将是(56 + 12)/ 12。第三行是(12)/ 12。第二行是(24 + 23)/ 12等。我需要确定给定ID的最大移动平均值的月份(和值)。

这是我在SQL本身甚至可以做的事情,还是我需要导出数据集并使用其他方法?有数百万行,所以如果可以的话,我想在SQL中这样做。我已经看了一些MA方法,我不确定它们是否适用于我正在尝试的方法。

我使用的SQL是与Teradata一起使用的派生物。它支持我需要使用的大多数标准功能。

3 个答案:

答案 0 :(得分:4)

只需使用子查询作为表达式:

SELECT id, 
       dt, 
       val, 
       (
        SELECT SUM(val)/12 
        FROM mytable t2 
        WHERE t2.id = t.id
          AND t2.dt > DATEADD(mm, -12, t.dt) 
          AND t2.dt < t.dt
       ) val12MonthAvg 
FROM mytable t

然而,对于数百万或行,它可能会非常慢。

答案 1 :(得分:1)

假设:

  • 您的日期格式为m / d / yyyy(我使用的格式为mm / dd / yyyy)
  • 此表上的id是其他实体的FK,其中id是PK
  • 您打算获取所选行的日期,并查找该行以及该ID不到12个月的所有行,并将这些行中的值相加

我会在Oracle SQL中写这个,因为那是我正在使用的,你没有指定;)

查询摘要:

  • “Chosen”是您的表的实例,用作输入行
  • “回顾”会收集所有行,包括您选择的行以及最多12个月的减去1天
  • 总结一下lookback.val的答案
WITH DateTable
      AS (SELECT 'a' id, TO_DATE ('01/01/2012', 'mm/dd/yyyy') dt, 23 val FROM DUAL
            UNION
            SELECT 'a', TO_DATE ('1/1/2012', 'mm/dd/yyyy'), 23 FROM DUAL
            UNION
            SELECT 'a', TO_DATE ('02/01/2012', 'mm/dd/yyyy'), 24 FROM DUAL
            UNION
            SELECT 'a', TO_DATE ('06/01/2013', 'mm/dd/yyyy'), 12 FROM DUAL
            UNION
            SELECT 'a', TO_DATE ('07/01/2013', 'mm/dd/yyyy'), 56 FROM DUAL
            UNION
            SELECT 'b', TO_DATE ('01/01/2009', 'mm/dd/yyyy'), 34 FROM DUAL
            UNION
            SELECT 'b', TO_DATE ('03/01/2009', 'mm/dd/yyyy'), 78 FROM DUAL)
SELECT chosen.id, chosen.dt, SUM (lookback.val)/12
  FROM DateTable chosen, DateTable lookback
 WHERE   chosen.id = 'a' --your input id
         AND chosen.dt = TO_DATE ('07/01/2013', 'mm/dd/yyyy') --your input date
         AND chosen.id = lookback.id
         AND lookback.dt > ADD_MONTHS (chosen.dt, -12)
         AND lookback.dt <= chosen.dt
GROUP BY chosen.id, chosen.dt;

如果您想查询任何行中不存在的日期/月份,请执行以下操作:

WITH DateTable
      AS (SELECT 'a' id, TO_DATE ('01/01/2012', 'mm/dd/yyyy') dt, 23 val FROM DUAL
            UNION
            SELECT 'a', TO_DATE ('1/1/2012', 'mm/dd/yyyy'), 23 FROM DUAL
            UNION
            SELECT 'a', TO_DATE ('02/01/2012', 'mm/dd/yyyy'), 24 FROM DUAL
            UNION
            SELECT 'a', TO_DATE ('06/01/2013', 'mm/dd/yyyy'), 12 FROM DUAL
            UNION
            SELECT 'a', TO_DATE ('07/01/2013', 'mm/dd/yyyy'), 56 FROM DUAL
            UNION
            SELECT 'b', TO_DATE ('01/01/2009', 'mm/dd/yyyy'), 34 FROM DUAL
            UNION
            SELECT 'b', TO_DATE ('03/01/2009', 'mm/dd/yyyy'), 78 FROM DUAL),
     InputData
      AS (SELECT 'b' id, TO_DATE ('12/15/2009', 'mm/dd/yyyy') dt FROM DUAL)
SELECT InputData.id, InputData.dt, SUM (lookback.val)/12
  FROM DateTable lookback, InputData
 WHERE  lookback.id = InputData.id
    AND lookback.dt > ADD_MONTHS (InputData.DT, -12)
    AND lookback.dt <= InputData.DT
     GROUP BY InputData.id, InputData.dt;

答案 2 :(得分:0)

我认为你应该看一下Teradata中的Windowing函数。 (注意:所有最新的ANSI SQL投诉数据库都支持窗口函数,以使用户能够处理逐行操作而不是基于集合的操作。)

所以,使用窗口函数我会写这样的东西:

SELECT ID ,DT ,VAL ,(SUM(VAL)OVER(PARTITION BY YEAR(DT)) )/12.00 AS L12M_mov_avg FROM some.table;

上面的代码没有经过测试 - 但是,只是强调使用窗口函数。