【发布时间】:2020-09-25 00:05:19
【问题描述】:
我的每台机器都有两个计数,p 和 r。
p 应该总是大于或等于r,但由于技术滞后和聚合周期短,情况并非总是如此,r 计数经常 - 但是并非总是 - 显示上一时期的数据。因为滞后的长度不是恒定的,所以无法确切知道r 值属于哪个时期。因此,我不能简单地将所有 r 计数统一向后移动,因为这可能会产生以前没有的其他差异。
这种情况无法改变,我必须按原样处理数据。
在下面的示例中,您可以看到 p 在机器 1 上短暂“暂停”,在机器 2 上显着减慢,但 r 计数继续返回更大的值在“暂停”之前比p 短时间:
-- Dummy data
declare @t table(d date,m int,p int,r int);
insert into @t values(getdate()-9,1,100,10),(getdate()-8,1,90 ,10),(getdate()-7,1,70 ,10),(getdate()-6,1,70 ,10),(getdate()-5,1,80 ,10),(getdate()-4,1,50 ,10),(getdate()-3,1,10 ,10),(getdate()-2,1,0 ,10),(getdate()-1,1,0 ,10),(getdate()+0,1,0 ,10),(getdate()+1,1,0 ,0),(getdate()+2,1,0 ,0),(getdate()+3,1,40 ,0),(getdate()+4,1,50 ,0),(getdate()+5,1,80 ,10),(getdate()-9,2,1100,100),(getdate()-8,2,190 ,100),(getdate()-7,2,170 ,100),(getdate()-6,2,170 ,100),(getdate()-5,2,180 ,100),(getdate()-4,2,150 ,100),(getdate()-3,2,110 ,100),(getdate()-2,2,10 ,100),(getdate()-1,2,10 ,100),(getdate()+0,2,10 ,100),(getdate()+1,2,10 ,0),(getdate()+2,2,10 ,0),(getdate()+3,2,140 ,0),(getdate()+4,2,150 ,0),(getdate()+5,2,180 ,100);
select * from @t order by m,d;
-- Output
+------------+---+------+-----+
| d | m | p | r |
+------------+---+------+-----+
| 2020-05-27 | 1 | 100 | 10 |
| 2020-05-28 | 1 | 90 | 10 |
| 2020-05-29 | 1 | 70 | 10 |
| 2020-05-30 | 1 | 70 | 10 |
| 2020-05-31 | 1 | 80 | 10 |
| 2020-06-01 | 1 | 50 | 10 |
| 2020-06-02 | 1 | 10 | 10 |
| 2020-06-03 | 1 | 0 | 10 |
| 2020-06-04 | 1 | 0 | 10 |
| 2020-06-05 | 1 | 0 | 10 |
| 2020-06-06 | 1 | 0 | 0 |
| 2020-06-07 | 1 | 0 | 0 |
| 2020-06-08 | 1 | 40 | 0 |
| 2020-06-09 | 1 | 50 | 0 |
| 2020-06-10 | 1 | 80 | 10 |
| 2020-05-27 | 2 | 1100 | 100 |
| 2020-05-28 | 2 | 190 | 100 |
| 2020-05-29 | 2 | 170 | 100 |
| 2020-05-30 | 2 | 170 | 100 |
| 2020-05-31 | 2 | 180 | 100 |
| 2020-06-01 | 2 | 150 | 100 |
| 2020-06-02 | 2 | 110 | 100 |
| 2020-06-03 | 2 | 10 | 100 |
| 2020-06-04 | 2 | 10 | 100 |
| 2020-06-05 | 2 | 10 | 100 |
| 2020-06-06 | 2 | 10 | 0 |
| 2020-06-07 | 2 | 10 | 0 |
| 2020-06-08 | 2 | 140 | 0 |
| 2020-06-09 | 2 | 150 | 0 |
| 2020-06-10 | 2 | 180 | 100 |
+------------+---+------+-----+
我需要能够在一定程度上及时向后调整这些 r 计数,以便将它们添加到前面的行中,以使每个 p 数字大于或等于相应的 r价值。
在上述m = 1 的示例中,输出可能类似于以下r 计数中的任何;我不关心调整的范围,只关心每一行的p >= r,并且调整只会及时倒退:
+------------+---+------+------+------+------+
| d | m | p | r1 | r2 | r3 |
+------------+---+------+------+------+------+
| 2020-05-27 | 1 | 100 | 10 | 10 | 10 |
| 2020-05-28 | 1 | 90 | 10 | 10 | 10 |
| 2020-05-29 | 1 | 70 | 10 | 15 | 10 |
| 2020-05-30 | 1 | 70 | 20 | 20 | 10 |) Note how the original 30 r counts
| 2020-05-31 | 1 | 80 | 20 | 20 | 10 |} that didn't follow the rule
| 2020-06-01 | 1 | 50 | 20 | 15 | 40 |) have been moved back in time
| 2020-06-02 | 1 | 10 | 10 | 10 | 10 |
| 2020-06-03 | 1 | 0 | 0 | 0 | 0 |
| 2020-06-04 | 1 | 0 | 0 | 0 | 0 |
| 2020-06-05 | 1 | 0 | 0 | 0 | 0 |
| 2020-06-06 | 1 | 0 | 0 | 0 | 0 |
| 2020-06-07 | 1 | 0 | 0 | 0 | 0 |
| 2020-06-08 | 1 | 40 | 0 | 0 | 0 |
| 2020-06-09 | 1 | 50 | 0 | 0 | 0 |
| 2020-06-10 | 1 | 80 | 10 | 10 | 10 |
+------------+---+------+------+------+------+
我已尝试使用窗口函数和rows between 等解决此问题,但我不知道如何识别需要重新分配给前一周期的r 值,以及识别哪个@987654345 @ 值来分配它们。如果我取得任何进展,我会在下面添加,但非常感谢所有帮助。
尝试 1
我管理的最接近的是以下适用于上述情况,但是当您将p = 50 值更改为小于40 并且当我只想向后调整时,也会及时向前和向后调整:
with t as(
select row_number() over (partition by m order by d) as rn
,(row_number() over (partition by m order by d)-1) / 5 as gn
,*
from @t
where m = 1
)
select *
,case when p > r
then r + (sum(case when p < r then r else 0 end) over (partition by gn) / sum(case when p > r then 1 else 0 end) over (partition by gn))
else case when p = r
then r
else 0
end
end as r_adj
from t;
尝试 2
这更接近了,但仍在向前和向后调整时间:
with t as(
select row_number() over (partition by m order by d) as rn
,(row_number() over (partition by m order by d)-1) / 10 as gn
,(row_number() over (partition by m order by d)+4) / 10 as gn2
,*
from @t
where m = 1
)
,r1 as(
select *
,case when p > r
then r + (sum(case when p < r then r - p else 0 end) over (partition by gn) / sum(case when p > r then 1. else 0. end) over (partition by gn))
else case when p = r
then r
else 0
end
end as r_adj
from t
)
select d
,m
,p
,r
,case when p > r_adj
then r_adj + (sum(case when p < r_adj then r_adj - p else 0 end) over (partition by gn2) / sum(case when p > r_adj then 1. else 0. end) over (partition by gn2))
else case when p = r_adj
then r_adj
else r_adj - (r_adj - p)
end
end as r_new
from r1
order by rn
;
【问题讨论】:
-
你能把你的SQL代码放上来吗?
-
我关心的是如何从源代码生成 r 字段。
-
r字段是源。正如我所说,它如您在上面看到的那样到达,需要相应地调整以适应p >= r规则。 -
如果你给我看的是源表,那么你必须从那里回溯。您需要了解如何将 r 与“主键”的一种形式相关联,以便确定哪些记录是错误的,以及需要偏移多少行。目标是将 r 与适当的字段“重新对齐”。如果是我,我会要求填写该表的人对其进行量化。
-
正如我在 OP 中所说:这种情况无法改变,我必须按原样处理数据。 并非世界上所有的数据都来自另一个数据库。在这种情况下,数据直接来自连接到现实世界机器的传感器并按原样到达。没有“主键”可以回溯,也无法更改传感器的输出。
标签: sql sql-server time-series sql-server-2016 window-functions