SQL 中每个分区的 last_value 和窗口函数的总和

Posted

技术标签:

【中文标题】SQL 中每个分区的 last_value 和窗口函数的总和【英文标题】:Sum of last_value of each partition in SQL with window functions 【发布时间】:2021-02-28 05:58:57 【问题描述】:

我有一个表,用于存储每个实体在任何时间点使用的总磁盘数。我想找到一个时间段内使用的峰值磁盘。 例如,数据看起来像

注意:时间戳是具有秒精度的实际时间戳,为简洁起见,我将其设置为上午 10 点等

timestamp | entity_id | disk_used
---------------------------------
    9am   |         1 |  10
   10am   |         2 |  20
   11am   |         2 |  15
   12am   |         1 |  12
     

在本例中,使用的最大磁盘数为 30(实体 1 为 10,实体 2 为 20)。

我尝试了很多方法。

    (每个实体的最大值)的总和不起作用,因为它会给出结果 20 + 12 = 32。但在实体 1 增加其大小之前,实体 2 减小了大小,因此峰值磁盘使用量为 30 . 我尝试使用窗口函数找到每个实体的last_value之和
select timestamp, entity_id,
    disk_used, 
    sum(last_value(disk_used) over(
        partition by entity_id order by timestamp)
    ) sum_of_last

正在尝试生成,因此我可以将其最大化,

timestamp | entity_id | disk_used | sum_of_last
-----------------------------------------------
    9am   |         1 |  10       |   10
   10am   |         2 |  20       |   30
   11am   |         2 |  15       |   25       // (10 + 15)
   12am   |         1 |  12       |   27       // (12 + 15)
     

但是,该查询不起作用,因为我们无法通过 ISO 标准 SQL 2003 中的窗口函数进行聚合。我正在使用 Amazon timestream db。查询引擎与 ISO 标准 SQL 2003 兼容。

-- 重新表述相同的问题,在每个时间戳我们都有数据点,用于该时刻使用的总磁盘。 要找到当时使用的总磁盘总量,请将每个实体的最后一个值相加。

有没有一种有效的方法来计算这个?

【问题讨论】:

(1) SQL 2003?请标记您正在使用的数据库。 (2) 你想要什么结果? 我们使用 amazon timestream,它是来自 amazon 的新时间序列数据库。我听到的查询与SQL 2003兼容。(并且有一些附加功能) 。 .也许引用的是 ISO 标准 SQL 2003。如果没有引用,只是“sql 2003”似乎很尴尬。 【参考方案1】:

如果你只有两个实体,你可以这样做:

select t.*,
       (last_value(case when entity_id = 1 then disk_used end ignore nulls) over (order by time) +
        last_value(case when entity_id = 2 then disk_used end ignore nulls) over (order by time)
       ) as total        
from t;

对所有实体进行概括的一种方法是每次为每个实体生成一行,估算值并聚合:

select ti.time, e.entity_id,
       last_value(disk_used ignore nulls) over (partition by e.entity_id order by t.time) as imputed_disk_used
from (select distinct time from t) ti cross join
     (select distinct entity_id from t) e left join
     t
     on ti.time = t.time and e.entity_id = t.entity_id;

然后就可以聚合了:

select time, sum(imputed_disk_used)
from (select ti.time, e.entity_id,
             last_value(disk_used ignore nulls) over (partition by e.entity_id order by t.time) as imputed_disk_used
      from (select distinct time from t) ti cross join
           (select distinct entity_id from t) e left join
           t
           on ti.time = t.time and e.entity_id = t.entity_id
     ) te
group by time;

但是,这给出了 per time 而不是 per time 和 entity_id 的值。

【讨论】:

这不起作用,因为在我给出的示例中,它几乎等于使用​​的磁盘。 对不起,实体的数量可能有几十万。我给出了简化的示例来重现 *** 通常推荐的问题。【参考方案2】:

我要查找某个时间段内使用的峰值磁盘

您可以使用两个级别的聚合:

select max(sum_disk_used)
from (
    select time, sum(disk_used) as sum_disk_used
    from mytable
    group by time
) t

子查询计算每个时间点的总disk_used,然后外部查询只获取峰值。

如果您的数据库支持某种limit 子句,这可以简化:

select time, sum(disk_used) as sum_disk_used
from mytable
group by time
order by sum_disk_used limit 1

要过滤给定的时间段,您通常会在子查询中添加 where 子句。

【讨论】:

这行不通,我表中的时间戳是一个时间戳,为简洁起见,我将其设为上午 10 点、上午 11 点等。 我想找出一整天的磁盘使用高峰。 @JackDaniels:time 列的数据类型对我提供的查询没有影响。请解释您得到的结果有什么问题。 我想我会改变这个例子,假设第一行的时间是上午 9 点。在这种情况下,您的示例将给出 20 的结果,而不是我们想要的 30。

以上是关于SQL 中每个分区的 last_value 和窗口函数的总和的主要内容,如果未能解决你的问题,请参考以下文章

SQL查询获取分区里最大和最小值_first_value/last_value

LAST_VALUE with IF statement inside not backfill it's partition --> 在选择每个分区的第一行时丢失最后一个值(BigQuery/

MySql窗口函数

雪花窗函数 last_value 和 max

窗口分析函数19_Mysql查询窗口函数里第一个最后一个第N个元素的值值案例详解(FIRST_VALUE LAST_VALUE NVH_VALUE)

SQL Server 分区窗口中多个属性的 Min() 和 Max()