查询以返回连续 X 天内值的最低 SUM

Posted

技术标签:

【中文标题】查询以返回连续 X 天内值的最低 SUM【英文标题】:Query to return the lowest SUM of values over X consecutive days 【发布时间】:2019-02-07 08:19:56 【问题描述】:

我什至不知道该怎么说这个!...

我有一个包含两列的表,即价格(双精度)和开始日期(日期)。我需要能够查询表并返回 X 行,对于这个例子来说 3 - 我需要拉回具有连续日期的 3 行,例如2019 年 5 月 7 日、8 日、9 日,这些日期范围内的总价格值最低。

我正在考虑一个需要 startDateRange、endDateRange、持续时间的函数。

它将返回 startDateRange 和 endDateRange 之间的行数(持续时间),而这三行的总和将是该日期范围内连续日期的任意行数中最便宜(最低)的总和。

例如,如果我想要 2019 年 5 月 1 日至 2019 年 5 月 14 日之间最便宜的 3 个日期,则将返回突出显示的 3 行;

我认为 LEAD() 和 LAG() 可能是一个起点,但我并不是真正的 SQL 人,所以不确定是否有更好的解决方法。

我目前在我的业务层上开发了一些 c# 来执行此操作,但在大型数据集上它有点迟钝 - 直接从我的数据层获取记录列表会很好。

任何想法将不胜感激!

提前致谢。

【问题讨论】:

最便宜的如何计算?连续三行的平均值? 我现在正在使用 SQL Server Management Studio 2014 @jarlh。 andomar 已经通过他的查询 @salman-a 几乎破解了我所追求的内容,现在只是想在我的 dbms 中更好地理解查询,将其分解为 3 行 【参考方案1】:

您可以使用窗口函数计算 3 天的平均值。然后使用top 1 选择平均值最低的 3 行集合:

select  top 1 StartDt
,       AvgPrice
from    (
        select  StartDt
        ,       avg(Price) over (order by StartDt rows between 2 preceding 
                                 and current row) AvgPrice
        ,       count(*) over (order by StartDt rows between 2 preceding
                               and current row) RowCnt
        from    prices
        ) sets_of_3_days
where   RowCnt = 3  -- ignore first two rows
order by
        AvgPrice desc

【讨论】:

哦哦!这几乎就是我所追求的,非常感谢 - 我已经将 avg(Price) 换成了 sum(Price) - 这让我获得了 X 天总数的记录。但是有一个障碍,它带来了连续 3 天最便宜的总价格的一行......即 2019 年 5 月 12 日:1531 英镑(总计 480,510,541) - 有没有办法单独拉回三行(我的屏幕截图中的第 10、11、12 行?)再次感谢!从来没有使用过前面的命令或类似的东西! 此解决方案仅在行连续时才考虑日期是否连续。证明?将第 10 天的日期更新到第 9 天并运行此过程【参考方案2】:

这是您的解决方案,当您开始声明日期时,逻辑就开始了。一切顺利。

--table example
declare @laVieja table (price float,fecha date  )

insert into @laVieja values (632,'20150101')
insert into @laVieja values (649,'20150102')
insert into @laVieja values (632,'20150103')
insert into @laVieja values (607,'20150104')
insert into @laVieja values (598,'20150105')
insert into @laVieja values (624,'20150106')
insert into @laVieja values (641,'20150107')
insert into @laVieja values (598,'20150108')
insert into @laVieja values (556,'20150109')
insert into @laVieja values (480,'20150110')
insert into @laVieja values (510,'20150111')
insert into @laVieja values (541,'20150112')
insert into @laVieja values (634,'20150113')
insert into @laVieja values (634,'20150114')
-- end of setting up table example





--declaring dates
declare @fechaIni date, @fechaEnds date 
set @fechaIni = '20150101'
set @fechaEnds = '20150114'

--assigning order based on price
select * , ROW_NUMBER() over (order by price) as unOrden
into #laVieja
from @laVieja
where fecha between @fechaIni and @fechaEnds 

-- declaring variables for cycle 
declare @iteracion float = 1 ,@iteracionMaxima float, @fechaPrimera date, @fechaSegunda date, @fechaTercera date
select @iteracionMaxima = max(unOrden) from #laVieja


--starting cycle
while(@iteracion <= @iteracionMaxima)
begin

        --assigning dates to variables 
        select @fechaPrimera = fecha from #laVieja where unOrden = @iteracion 
        select @fechaSegunda = fecha from #laVieja where unOrden = @iteracion + 1 
        select @fechaTercera = fecha from #laVieja where unOrden = @iteracion + 2 

        --comparing variables 
        if(@fechaTercera = DATEADD(day,1,@fechaSegunda) and @fechaSegunda = DATEADD(day,1,@fechaPrimera))
        begin 

            select * from #laVieja
            where unOrden in (@iteracion,@iteracion+1,@iteracion+2)

        set @iteracion = @iteracionMaxima
        end 

set @iteracion +=1
end 

【讨论】:

嗨,谢谢,是的,我认为我的解释令人困惑!它需要有连续的日子,这会给我最便​​宜的 3 天,但它们不一定是连续的(例如 11 日、12 日、13 日)。干杯 嗨jezorama,您需要做的就是通过分配开始日期和结束日期来设置代码的限制。鉴于日期是连续的,查询应该返回 3 个最小值。 太好了,谢谢,这正是我所追求的 - 看起来比 @andomar 的解决方案复杂一点 - 我现在就给它一个爆炸。我也可以用我的 SQL 学习一些西班牙语:D 哈哈,这也是代码的副作用 :) 很高兴它能达到目的。 select top 1 StartDt , AvgPrice from ( select StartDt , sum(Price) over (order by StartDt rows between 2 before and current row) AvgPrice , count(1) over (order by StartDt rows between 2前一行和当前行) RowCnt from #laVieja2 ) sets_of_3_days where RowCnt = 3 -- 忽略前两行按 AvgPrice 排序【参考方案3】:

您可以使用带有OVER (... ROWS BETWEEN) 子句的窗口函数来计算特定行数的总和/平均值。然后您可以使用ROW_NUMBER 查找其他两行。

WITH cte1 AS (
    SELECT *
         , SUM(Price) OVER (ORDER BY Date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS wsum
         , ROW_NUMBER() OVER (ORDER BY Date) AS rn
    FROM #t
), cte2 AS (
    SELECT TOP 1 rn
    FROM cte1
    WHERE rn > 2
    ORDER BY wsum, Date
)
SELECT *
FROM cte1
WHERE rn BEtWEEN (SELECT rn FROM cte2) - 2 AND (SELECT rn FROM cte2)

在上述查询中,将 2 替换为 窗口大小 - 1

【讨论】:

phwoaarrrr。史诗。我正在根据日期和价格执行一些可怕的 IN 子句,以从 SUM 行中获取我想要的 3 行的列表,但显然,我应该远离 SQL,因为这就是你的做法!非常感谢! 有没有办法让这个部分中的数值动态化:“ORDER BY Date ROWS BETWEEN 2 PRECEDING”,即数字“2”?我似乎无法用变量替换那个 2 值。再次感谢。 看起来我必须通过环顾四周来动态构建 SQL:***.com/questions/29695746/… 您可以使用相关子查询,或者交叉应用。但那些表现不佳。 见dbfiddle.uk/…

以上是关于查询以返回连续 X 天内值的最低 SUM的主要内容,如果未能解决你的问题,请参考以下文章

Mysql查询以查找一段时间内值的百分比变化

计算时间值的 SUM() 以字符串形式返回时间值

sql: 按 x,y,z 分组;返回按 f(z) 最低的 x,y 分组

和为K的子数组

和为K的子数组

返回 IN 值的默认结果,不管