Выберите среднее значение по продукту, используя дату возврата - PullRequest
1 голос
/ 18 апреля 2019

Я запрашиваю среднемесячную ставку по всем продуктам, среднюю по всем пятницам за месяц.Мой скрипт таблицы и данных:

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

После завершения.

Пожалуйста, помогите мне решить эту проблему.Благодарю.

Ответы [ 2 ]

1 голос
/ 18 апреля 2019

Это могут быть минимальные изменения для достижения новых требований:

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
1 голос
/ 18 апреля 2019

Используя 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...