2016-08-24 103 views
0

我在SQL Server中遇到任务问题。表格的名称是'test'。实现3天移动平均线

CREATE TABLE test 
(
    [entry_date] [date] NOT NULL, 
    [value] [int] NOT NULL, 
    [id] [int] NULL, 
) 

INSERT test VALUES ('2012-02-01', 10, 1); 
INSERT test VALUES ('2012-02-02', 20, 2); 
INSERT test VALUES ('2012-02-03', 10, 1); 
INSERT test VALUES ('2012-02-04', 30, 2); 
INSERT test VALUES ('2012-02-05', 10, 1); 
INSERT test VALUES ('2012-02-06', 11, 3); 
INSERT test VALUES ('2012-02-06', 40, 1); 
INSERT test VALUES ('2012-02-07', 10, 2); 
INSERT test VALUES ('2012-02-08', 50, 3); 
INSERT test VALUES ('2012-02-09', 10, 2); 
INSERT test VALUES ('2012-02-10', 60, 2); 
INSERT test VALUES ('2012-02-10', 50, 4); 
INSERT test VALUES ('2012-02-11', 51, 3); 

基于以上数据,我希望得到的输出象下面这样:

entry_date |value |id |averagevaluesof_3days 
-----------+------+---+---------------------- 
2012-02-01 |10 |1 | 0  -----here we donot have last3rdaysdate 
2012-02-03 |10 |1 | 6.6-------last 3days feb3 and feb2 and feb1 
2012-02-05 |10 |1 | 6.6 
2012-02-06 |40 |1 | 0 ----here we donot have last3rdaysdate(4th date is missed) 
2012-02-02 |20 |2 | 0 
2012-02-04 |30 |2 | 16.6 
2012-02-07 |10 |2 | 0 
2012-02-09 |10 |2 | 6.6 
2012-02-10 |60 |2 | 0 
2012-02-06 |11 |3 | 0 
2012-02-08 |50 |3 | 20.3 
2012-02-11 |51 |3 | 0 
2012-02-10 |50 |4 | 0 

我需要根据ID明智的日期(ENTRY_DATE)来计算的信息运行的最后三天的平均值。

如果ENTRY_DATE不要在日期(ENTRY_DATE)的信息,最后3天有再考虑,其历史平均水平0.1

让我提供了一个例子进一步解释我的目标:

here we need to consider based on id wise 
if we consider id=1 related information 

- If the `Entry_date: 2012-02-01`, this date have value and back 2 days don't have value less than 2012-02-01 average value is 0 

- If the `Entry_date: 2012-02-03`, this date have value and back 2 days means 2012-02-01 and 2012-01-02. Here we can take 3 days average for 2012-02-03 date is 6.6 ((10+0+10)/3 =6.6) if we take 2012-02-05 is 6.6 ((10+0+10)/3 = 6.6) 
- If the `Entry_date: 2012-02-01`, this date have value and back 2 days don't have value less than 2012-02-01 average value is 0 

if we consider id=2 related information 
- If the `Entry_date: 2012-02-02`, this date have value and back 2 days means 2012-02-01 and 2012-01-31,don't have value then average value is 0 
- If the `Entry_date: 2012-02-04`, this date have value and back 2 days means 2012-02-03 and 2012-02-02. Here we can take 3 days average for 2012-02-04 date is 16.6 ((30+0+20)/3 =16.6) if we take 2012-02-07 is 0 because of last 3rd date dont have 

- If the `Entry_date: 2012-02-09`, this date have value and back 2 days means 2012-02-08 and 2012-02-07. Here we can take 3 days average for 2012-02-09 date is 6.6 ((10+0+10)/3 =6.6) if we take 2012-02-10 is 0 because of last 3rd date dont have 

请告诉我如何编写一个查询来为SQL Server实现这个任务。

+0

你用什么版的SQL Server?请只留下一个相关的标签。你的表中可能缺少一些日期吗?请用三行“2012-02-17”,“2012-02-18”,“2012-02-19”向你展示例子,并告诉我们结果应该如何。 –

+0

'2012-02-10'呢?有两个相同的日期? – NEER

+0

@ravikumar:请检查我的答案。 – StackUser

回答

0

试试这个

CREATE table #test 
(
    [entry_date] [date] NOT NULL, 
    [value] [int] NOT NULL, 
    [id] [int] NULL 
) 

INSERT #test VALUES ('2012-02-01', 10, 1); 
INSERT #test VALUES ('2012-02-02', 20, 2); 
INSERT #test VALUES ('2012-02-03', 10, 1); 
INSERT #test VALUES ('2012-02-04', 30, 2); 
INSERT #test VALUES ('2012-02-05', 10, 1); 
INSERT #test VALUES ('2012-02-06', 11, 3); 
INSERT #test VALUES ('2012-02-06', 40, 1); 
INSERT #test VALUES ('2012-02-07', 10, 2); 
INSERT #test VALUES ('2012-02-08', 50, 3); 
INSERT #test VALUES ('2012-02-09', 10, 2); 
INSERT #test VALUES ('2012-02-10', 60, 2); 
INSERT #test VALUES ('2012-02-10', 50, 4); 
INSERT #test VALUES ('2012-02-11', 51, 3); 

SELECT    
    CurrentRow.entry_date, 
    CurrentRow.value, 
    CurrentRow.id, 
    CASE 
     WHEN ISNULL(PriviousRow1.value, 0) = 0 OR 
      ISNULL(PriviousRow2.value, 0) = 0 THEN 0.00 
     ELSE 
      (ISNULL(CurrentRow.value, 0) + 
      ISNULL(PriviousRow1.value, 0) + 
      ISNULL(PriviousRow2.value, 0))/3.0 END averagevaluesof_3days 

FROM 
    #test CurrentRow LEFT JOIN 
    #test PriviousRow1 ON CONVERT(VARCHAR(8), DATEADD(DAY, -1, CurrentRow.entry_date), 112) = 
          CONVERT(VARCHAR(8), PriviousRow1.entry_date, 112) LEFT JOIN 
    #test PriviousRow2 ON CONVERT(VARCHAR(8), DATEADD(DAY, -2, CurrentRow.entry_date), 112) = 
          CONVERT(VARCHAR(8), PriviousRow2.entry_date, 112) 
ORDER BY 
    CurrentRow.entry_date 

结果:

entry_date  value  id   averagevaluesof_3days 
----------  ----------- ----------- --------------------------------------- 
2012-02-01  10   1   0.00 
2012-02-02  20   2   0.00 
2012-02-03  10   1   13.33 
2012-02-04  30   2   20.00 
2012-02-05  10   1   16.66 
2012-02-06  11   3   17.00 
2012-02-06  40   1   26.66 
2012-02-07  10   2   10.33 
2012-02-07  10   2   20.00 
2012-02-08  50   3   23.66 
2012-02-08  50   3   33.33 
2012-02-09  10   2   23.33 
2012-02-10  60   2   40.00 
2012-02-10  50   4   36.66 
2012-02-11  51   3   40.33 
2012-02-11  51   3   37.00 
+0

注意:您的样本数据和预期数据不匹配。 – NEER

1

尝试这样,

SELECT x.entry_date 
    ,x.value 
    ,x.id 
    ,convert(DECIMAL(18, 2), CASE 
      WHEN (
        SELECT count(*) 
        FROM test 
        WHERE id = x.id 
         AND entry_date <= (x.entry_date) 
        ) >= 3 
       THEN avg(value) OVER (
         PARTITION BY id ORDER BY id 
          ,entry_date rows BETWEEN 2 preceding 
           AND CURRENT row 
         ) 
      ELSE 0 
      END) AS rollingavg 
FROM (
    SELECT id 
     ,entry_date 
     ,convert(DECIMAL(18, 2), sum(value)) AS value 
    FROM test 
    GROUP BY id 
     ,entry_date 
    ) x 
+0

这应该是最快的解决方案。 – PacoDePaco

+0

您在内部选择计数(*)中缺少'AND DATEDIFF(day,entry_date,x.entry_date)<3'。目前它忽略了日期中的差距。 – adrianm

+0

@Ravikumar:请选择最适合您的答案。 – StackUser