当前行和上一行之间具有特定值的窗口函数
Posted
技术标签:
【中文标题】当前行和上一行之间具有特定值的窗口函数【英文标题】:Window Functions between current row and previous row with specific value 【发布时间】:2019-08-27 19:19:09 【问题描述】:我希望能够将当前行与最新的前一行之间的某一列中的值与另一列中的某个值相加。
在此示例中,我想对当前行和最新行之间的 Val 列求和,RecType 为 2,按 RowNum 排序的 ID 分区。
DECLARE @ExampleTable TABLE
(
Id INT,
RowNum INT,
RecType INT,
Val INT
)
INSERT INTO @ExampleTable
(Id, RowNum, RecType, Val)
VALUES
(1, 1, 1, 1),
(1, 2, 2, 2),
(1, 3, 1, 4),
(1, 4, 1, 8),
(1, 5, 1, 16),
(1, 6, 2, 32),
(1, 7, 1, 64),
(2, 1, 2, 1),
(2, 2, 2, 2),
(2, 3, 1, 4),
(2, 4, 1, 8),
(2, 5, 1, 16),
(2, 6, 1, 32),
(2, 7, 2, 64)
我希望得到如下结果:
DECLARE @Results TABLE
(
Id INT,
RowNum INT,
SumSinceLast2 INT
)
INSERT INTO @Results
(Id, RowNum, SumSinceLast2)
VALUES
(1, 1, 0),
(1, 2, 0),
(1, 3, 6), -- 4 + 2
(1, 4, 14), -- 4 + 2 + 8
(1, 5, 30), -- 16 + 8 + 4 + 2
(1, 6, 62), -- 32 + 16 + 8 + 4 + 2
(1, 7, 96), -- 64 + 32
(2, 1, 0),
(2, 2, 3), -- 2 + 1
(2, 3, 6), -- 4 + 2
(2, 4, 14), -- 8 + 4 + 2
(2, 5, 30), -- 16 + 8 + 4 + 2
(2, 6, 62), -- 32 + 16 + 8 + 4 + 2
(2, 7, 126) -- 64 + 32 + 16 + 8 + 4 + 2
这是我应该能够在 SQL Server 2017 中轻松完成的事情吗?我希望在这里可以使用窗口函数。
【问题讨论】:
我觉得逻辑有点混乱。值为“2”的行与前2行相加?val
=32 相加两次,在RowNum
s 的两组id
1 中,对吗?
@Serg 正确 - 因为我想将当前行与所有先前行相加,直到并包括前一行,rec 类型为 2。所以第 6 行包含它,因为该窗口位于行之间第 2 行和第 6 行。第 7 行包含它,因为该窗口应该在第 6 行和第 7 行之间。
【参考方案1】:
这并不能完全返回您想要的结果,但结果似乎更合理。每个“2”开始一个新组。然后将这些值在组内累加求和:
select e.*,
(case when grp_2 = 0
then 0
else sum(val) over (partition by id, grp_2 order by rownum)
end) as result
from (select e.*,
sum(case when RecType = 2 then 1 else 0 end) over
(partition by id
order by rownum
) as grp_2
from @ExampleTable e
) e
order by id, rownum;
Here 是一个 dbfiddle。
可以调整结果(这会使查询变得更加混乱)以按照您拥有它们的方式“修复”“2”的值。但是,这个版本对我来说更有意义,因为“2”不计入两个单独的组。
这是一个对“2”进行双重计算的调整版本:
select e.*,
(case when grp_2 = 0 or grp_2 = 1 and RecType = 2
then 0
when RecType <> 2
then sum(val) over (partition by id, grp_2 order by rownum)
else sum(val) over (partition by id, grp_2_desc) + lag(val) over (partition by id, Rectype order by rownum)
end) as result
from (select e.*,
sum(case when RecType = 2 then 1 else 0 end) over
(partition by id
order by rownum
) as grp_2,
sum(case when RecType = 2 then 1 else 0 end) over
(partition by id
order by rownum desc
) as grp_2_desc
from @ExampleTable e
) e
order by id, rownum;
【讨论】:
【参考方案2】:我知道已经有解决方案了,但是既然我写了代码,我还是要把它贴在这里。
--Sum the range
select
et.Id
,a.CurrentRow
,sum(CASE WHEN ClosestMinRow = CurrentRow THEN 0 ELSE et.Val end) --When there is no previous 2 then set them to 0
from
@ExampleTable et
join
(
--Create begin and end range
select
et.Id
,et.RowNum CurrentRow
,ISNULL(FloorRange.RowNum,et.RowNum) ClosestMinRow
from
@ExampleTable ET
OUTER Apply (
-- Get the RecType = 2 in order to create a range
select
MAX(RowNum) RowNum
from
@ExampleTable et2
WHERE
RecType = 2
AND et2.RowNum < ET.RowNum
AND et2.Id = et.Id
) FloorRange
) a
ON et.Id = a.Id
and et.RowNum between a.ClosestMinRow and CurrentRow
GROUP BY
et.Id
,a.CurrentRow
order by
et.Id
,a.CurrentRow
【讨论】:
以上是关于当前行和上一行之间具有特定值的窗口函数的主要内容,如果未能解决你的问题,请参考以下文章
在每个当前行和上一行 BigQuery 之间查找 MAX、AVG
当前行和上一行之间的秒数差异,并使用 google bigquery 将值存储在单独的列中