一种在一段时间内获得平均值的方法

时间:2014-09-14 01:37:05

标签: sql sql-server

我有一张五年的数据表。除其他值外,它还有JulianDateRefEToPrecipitation

我需要获得五年内RefETo的平均值和去年的平均值,加上去年的降水量之和。

同时,平均值和总和必须持续7到28天。

现在,我正在使用一个函数:

FUNCTION [dbo].[CIMISAvg](@Stn INT, @Yr INT,  @Period INT)
RETURNS @AvgTable TABLE (Period INT, RefETo float, RefETo1 float, Precipitation float)
AS
BEGIN
    DECLARE @PeriodInc INT = 1
    DECLARE @RefETo float
    DECLARE @Precip float
    DECLARE @RefETo1 float
    DECLARE @P INT = 1
    BEGIN
        WHILE @PeriodInc < 366
           BEGIN
              IF @PeriodInc < 365
                 BEGIN
                    SET @RefETo = (SELECT AVG(RefETo) FROM Cimis 
                     WHERE StationNo = @Stn AND RefETO >= 0 AND JulianDate BETWEEN @PeriodINC AND 
                     PeriodINC + @Period - 1)
                    SET @RefETo1 = (SELECT AVG(RefETo) FROM Cimis WHERE StationNo = @Stn AND 
                     RefETO >= 0 AND JulianDate BETWEEN @PeriodINC AND @PeriodINC + @Period - 1 
                     AND DATEPART(Year, DateCollected) = @Yr)
                    SET @Precip = (SELECT SUM(Precipitation) FROM Cimis 
                     WHERE StationNo = @Stn AND Precipitation >= 0 AND JulianDate 
                     BETWEEN @PeriodINC AND @PeriodINC + @Period - 1 
                     AND DATEPART(Year, DateCollected) = @Yr)
                 END
              ELSE
                 BEGIN
                    SET @RefETo = (SELECT AVG(RefETo) FROM Cimis 
                     WHERE StationNo = @Stn AND RefETO >= 0 
                     AND (JulianDate > 364 OR JulianDate < @Period - 1))                        
                    SET @RefETo1 = (SELECT AVG(RefETo) FROM Cimis WHERE StationNo = @Stn 
                     AND RefETO >= 0  AND JulianDate > 364 
                     AND DATEPART(Year, DateCollected) = @Yr)
                    SET @Precip = (SELECT SUM(Precipitation) FROM Cimis WHERE StationNo = @Stn 
                     AND Precipitation >= 0 AND JulianDate > 364 
                     AND DATEPART(Year, DateCollected) = @Yr)
                 END
              INSERT INTO @AvgTable(Period, RefETo, RefETo1, Precipitation) 
              VALUES (@P, @RefETo, @RefETo1, @Precip)
              SET @PeriodInc += @Period
              SET @P += 1
           END
        END
   RETURN
END

如果我使用,则返回下表:

SELECT * from dbo.CimiAvg(80,2014,28)

Period RefETo                RefETo1               Precipitation
1      0.0417192857142857    0.0470392857142857    0.0156
2      0.0672328571428571    0.0585214285714286    0
3      0.121372142857143     0.135967857142857     1.2755
4      0.170277519379845     0.186428571428571     0.7991
5      0.235207258064516     0.240425              0.7087
6      0.268260240963855     0.294403571428571     0.1811
7      0.293128125           0.290282142857143     0
8      0.273767123287671     0.267457142857143     0.0196
9      0.244358333333333     0.2513375             0
10     0.176087142857143     NULL                  NULL
11     0.10749               NULL                  NULL
12     0.0625579831932773    NULL                  NULL
13     0.0382158273381295    NULL                  NULL
14     0.0413401459854015    NULL                  NULL

哪个好,花花公子,但有没有人有更好的想法?

我已经摆弄

SELECT JulianDate, AVG(RefETO) 
                   OVER (ORDER BY JulianDate ROWS BETWEEN 28 PRECEDING AND CURRENT ROW) 
FROM Cimis 

并且有各种各样的变化但是没有任何地方

4 个答案:

答案 0 :(得分:1)

我同意评论中的PM 77-1。条件聚合是可行的方法。

试试这个:

Declare @Stn INT, @Yr INT,  @Period INT
Select @Stn = 80, @Yr=2014, @Period=28

SELECT Period, RefETo/RefEToDays AS RefETo, RefETo1/RefETo1Days AS RefETo1, Precipitation
FROM    (
    SELECT 
    ROUNDUP(JulianDate/@Period,0) AS Period,
    SUM(
      RefETo
    ) AS RefETo,
    SUM(
        CASE WHEN DATEPART(Year, DateCollected) = @Yr  THEN RefETo ELSE 0 END
    ) AS RefETo1,
    COUNT(*)
      AS RefEToDays,
    SUM(
        CASE WHEN DATEPART(Year, DateCollected) = @Yr THEN 1 ELSE 0 END
    ) AS RefETo1Days,
    SUM(
        CASE WHEN DATEPART(Year, DateCollected) = @Yr Then Precipitation ELSE 0 END
    ) AS Precipitation
    FROM Cimis
    WHERE StationNo = @Stn 
    GROUP BY ROUNDUP(JulianDate/@Period,0)
    ) c

答案 1 :(得分:0)

  

我需要在五年内获得RefETo的平均值   去年的平均值加上去年的降水量之和   年。

从我所处的位置来看,这是一个应该在报表编写器中实现的需求的完美示例,而不是在dbms中。

我不会在单个SQL语句中执行此操作。

答案 2 :(得分:0)

是的!为此,肯定有更好的,更有表现力的手段。

首先要确保您在数据库中定义了NUMBERS table,或者熟悉如何使用CTE动态生成一个(公用表表达式):

主查询现在变为:

DECLARE @NumPeriods as INT = 365 / @Period;

WITH
    -- vvv BEGIN Needed only in absence of a NUMBERS table
    E1(N) as (
        SELECT 1 FROM (VALUES  (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)  )E1(N)
    ),
    E2(N) as ( SELECT 1 FROM E1 a CROSS JOIN E1 b ),
    E4(N) as ( SELECT 1 FROM E2 a CROSS JOIN E2 b ),
      -- repeated for E8, E16, as needed
    -- ^^^ END Needed only in absence of a NUMBERS table

Tally(N) as (
    SELECT TOP(@NumPeriods) -- This is sufficient for our purposes 
        ROW_NUMBER() OVER (ORDER BY N) AS N 
    FROM E4
    UNION ALL
    SELECT 0
)
INSERT INTO @AvgTable(Period, RefETo, RefETo1, Precipitation) 
    SELECT
         p.Period
        ,AVG(RefETo) as RefETo
        ,SUM(case when DATEPART(Year, DateCollected) = @yr then RefETo else 0 end)
        /SUM(case when DATEPART(Year, DateCollected) = @yr then    1   else 0 end) 
                     as RefETo1
        ,SUM(case when DATEPART(Year, DateCollected) = @yr then Precipitation 
                                                           else 0 end)
                     as Precipitation
    FROM Cimis
    JOIN (
        SELECT
             N, N + 1           as Period
            ,(N*@Period) + 1 as StartDate
            ,CASE WHEN N * @Period > 363 THEN @Period - 1 ELSE (N + 1) * @Period END AS EndDate

        FROM Tally
    ) p
       ON JulianDate BETWEEN p.StartDate AND p.EndDate 
       OR ((JulianDate > 364 OR JulianDate < @Period - 1) AND p.N * @Period > 363)

    WHERE StationNo  = @Stn
      AND RefETO    >= 0 
    GROUP BY  p.Period
    PRDER BY  p.Period

我相信您会发现这种实现在大型数据集上要比当前实现快得多。

答案 3 :(得分:0)

我认为这是基于Jim V代码的最终版本。最后一个时期由UNION处理。

DECLARE @Stn INT, @Yr INT,  @Period INT
SELECT @Stn = 80, @Yr=2014, @Period=28

SELECT Period, RefETo, RefETo1, Precipitation
FROM (
SELECT CEILING(JulianDate / @Period) AS Period, AVG(RefETo) AS RefETo, 
AVG(CASE WHEN DATEPART(Year, DateCollected) = @Yr THEN RefETo ELSE NULL END) AS RefETo1, 
SUM(CASE WHEN DATEPART(Year, DateCollected) = @Yr Then Precipitation ELSE 0 END) AS
Precipitation 
FROM Cimis WHERE StationNo = @Stn AND JulianDate < 365
GROUP BY CEILING(JulianDate / @Period)) C
UNION SELECT 366 / @Period + 1 AS Period, AVG(RefETo) AS RefETo, 
(SELECT AVG(RefETo) FROM CIMIS 
WHERE DATEPART(Year, DateCollected) = @Yr AND JulianDate > 364) AS RefETo1,
(SELECT SUM(Precipitation) FROM CIMIS 
WHERE DATEPART(Year, DateCollected) = @Yr AND JulianDate > 364) AS Precipitation 
FROM Cimis WHERE StationNo = @Stn AND (JulianDate > 364 OR JulianDate < @Period - 1) 
ORDER BY Period