我要查询所有产品的月平均价格,即该月所有星期五的平均价格。我的表和数据脚本是:
CREATE TABLE [dbo].[Product_Entry](
[ProductCode] [varchar](10) NOT NULL,
[Rate] [decimal](18, 0) NULL,
[RateDate] [date] NULL
) ON [PRIMARY]
GO
INSERT [dbo].[Product_Entry] ([ProductCode], [Rate], [RateDate]) VALUES (N'050101', CAST(56 AS Decimal(18, 0)), CAST(N'2019-04-05' AS Date))
GO
INSERT [dbo].[Product_Entry] ([ProductCode], [Rate], [RateDate]) VALUES (N'050102', CAST(60 AS Decimal(18, 0)), CAST(N'2019-04-05' AS Date))
GO
INSERT [dbo].[Product_Entry] ([ProductCode], [Rate], [RateDate]) VALUES (N'050103', CAST(65 AS Decimal(18, 0)), CAST(N'2019-04-04' AS Date))
GO
INSERT [dbo].[Product_Entry] ([ProductCode], [Rate], [RateDate]) VALUES (N'050101', CAST(50 AS Decimal(18, 0)), CAST(N'2019-04-12' AS Date))
GO
INSERT [dbo].[Product_Entry] ([ProductCode], [Rate], [RateDate]) VALUES (N'050102', CAST(64 AS Decimal(18, 0)), CAST(N'2019-04-11' AS Date))
GO
INSERT [dbo].[Product_Entry] ([ProductCode], [Rate], [RateDate]) VALUES (N'050103', CAST(70 AS Decimal(18, 0)), CAST(N'2019-04-12' AS Date))
GO
INSERT [dbo].[Product_Entry] ([ProductCode], [Rate], [RateDate]) VALUES (N'050101', CAST(55 AS Decimal(18, 0)), CAST(N'2019-04-15' AS Date))
GO
INSERT [dbo].[Product_Entry] ([ProductCode], [Rate], [RateDate]) VALUES (N'050102', CAST(50 AS Decimal(18, 0)), CAST(N'2019-04-16' AS Date))
GO
INSERT [dbo].[Product_Entry] ([ProductCode], [Rate], [RateDate]) VALUES (N'050103', CAST(68 AS Decimal(18, 0)), CAST(N'2019-04-17' AS Date))
GO
所以我制作了一个函数,该函数采用月和年并返回所有星期五
CREATE PROCEDURE [dbo].[GetallFridaysinMonth] (
@month VARCHAR(2) = NULL
,@year VARCHAR(5) = NULL
)
AS
BEGIN
SELECT Fridays = DATEADD(yy, DATEDIFF(yy, 0, '' + @year + '-' + @month + '-' + '01'), n.num)
INTO #t
FROM (
SELECT TOP 366 num = ROW_NUMBER() OVER (
ORDER BY a.NAME
) - 1
FROM dbo.syscolumns a
,dbo.syscolumns b
) n
WHERE DATENAME(weekday, DATEADD(yy, DATEDIFF(yy, 0, '' + @year + '-' + @month + '-' + '01'), n.num)) = 'Friday'
SELECT Fridays
FROM #t
WHERE datepart(month, Fridays) = @month
DROP TABLE #t
END
然后使用此功能查询平均值
BEGIN
CREATE TABLE #t121 (Fridays DATETIME,id INT IDENTITY(1, 1));
INSERT INTO #t121 (Fridays)
EXEC dbo.GetallFridaysinMonth @month,@year;
SELECT ProductCode
,convert(DECIMAL(18), avg(Rate)) AS AverageRate
FROM dbo.product_entry
WHERE RateDate IN (
SELECT Fridays
FROM #t121
)
GROUP BY ProductCode
DROP TABLE #t121
END
这很好,直到我的客户修改要求并说如果星期五没有费率,然后再检查周四,如果不是星期四,则检查周三,这意味着回溯到星期六以找到周率。
现在如果只有2或3种产品,那么我可以使用Case条件,但不知道如何回溯所有250种以上的产品。
我的演示数据的预期结果是
ProductCode AverageRate
-------------------------
050101 54
050102 58
050103 68
四舍五入后
请帮助我解决此问题。 谢谢。
答案 0 :(得分:1)
使用CTE和Row_Number()我先按产品代码对数据进行分区,然后按月中的星期几降序对月中的星期进行划分。 (我将查询设置为仅检索当前年份)。然后,仅查看第1行,即可按每周最后一天的产品代码进行平均。
;With cte1 As
(
Select
*,
Case When DatePart(dw,RateDate) = 7 Then 0 Else DatePart(dw,RateDate) End As dowN, --Day of week Number (Make Saturday = 0 instead of 7)
Datepart(day, datediff(day, 0, RateDate)/7 * 7)/7 + 1 As wom --Week of Month Number
From Product_Entry
Where Year(RateDate) = Year(GetDate()) --Current Year Only
), cte2 As
(
Select
Row_Number() Over (Partition By ProductCode, wom Order By dowN Desc) As rn,
*
From cte1
)
Select ProductCode, Cast(Round(AVG(Rate),0) As Int) As AverageRate From cte2
Where rn = 1
Group By ProductCode
答案 1 :(得分:1)
这些可能是达到新要求的最小更改:
begin
create table #t121 (Fridays datetime);
insert into #t121 (Fridays)
exec dbo.GetallFridaysinMonth @month, @year;
with AVGDaily as (
select ProductCode, RateDate, AVG(Rate) as AVGDay
from dbo.Product_Entry
where month(RateDate)=@month and year(RateDate)=@year
group by ProductCode, RateDate
)
select ProductCode
, convert(decimal(18), AVG(AVGRate)) as AverageRate
from (
select distinct t.Fridays, AVGDaily.ProductCode,
AVGRate=(
select top (1) AVGDay
from AVGDaily i2
where ProductCode = AVGDaily.ProductCode
and i2.RateDate between
DATEADD(DD, -6, t.Fridays) and t.Fridays
order by RateDate desc)
from AVGDaily, #t121 as t) g
group by ProductCode
drop table #t121
end
也许可以改进此过程:
CREATE Proc GetallFridaysinMonth(@month varchar(2), @year varchar(4), @dw tinyint=5) as
declare @dateStart datetime, @maxDDinMM tinyint
select @dateStart = cast(right('20'+@year,4)+right('0'+@month,2)+'01' as datetime)
, @maxDDinMM = DATEDIFF(DD, @dateStart, DATEADD(MM, 1, @dateStart)) - 1;
with ADD_cte as (
select 0 as AddDays
union all
select AddDays + 1 from ADD_cte where AddDays < @maxDDinMM
)
select DATEADD(DD, AddDays, @dateStart) as SalesDay
from ADD_cte
where (DATEPART(DW, DATEADD(DD, AddDays, @dateStart)) + @@DATEFIRST + 5) % 7 + 1 = @dw