我有一张五年的数据表。除其他值外,它还有JulianDate
,RefETo
和Precipitation
。
我需要获得五年内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
并且有各种各样的变化但是没有任何地方
答案 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