我有一个像这样的表(实际上是大查询的结果):
id
我需要这个结果:
id | date_measured | out_of_range
-----+-----------------------+--------------
3147 | 09/08/2019 20.00:00 | 1
3147 | 09/08/2019 21.00:00 | 0
3147 | 09/08/2019 22.00:00 | 0
3147 | 09/08/2019 23.00:00 | 1
3147 | 10/08/2019 00.00:00 | 1
3147 | 10/08/2019 01.00:00 | 1
3147 | 10/08/2019 02.00:00 | 0
3125 | 09/08/2019 20.00:00 | 0
3125 | 09/08/2019 21.00:00 | 1
3125 | 09/08/2019 22.00:00 | 1
3125 | 09/08/2019 23.00:00 | 0
3125 | 10/08/2019 00.00:00 | 1
3125 | 10/08/2019 01.00:00 | 1
3125 | 10/08/2019 02.00:00 | 1
这是值 out_of_range = 1 和相对的开始和结束日期的连续重复。
我尝试使用this解决方案,但是对于 out_of_range ,我只能没有连续的 1 。值。
答案 0 :(得分:1)
如果给每一行两个递增的数值-每个ROW_NUMBER
一个,另一个id
/ id
对,则使用out_of_range
分析函数。如果您从另一个中减去一个,那么在连续的行集中具有相同的id
/ out_of_range
值的结果数将是恒定的,您可以将其用于GROUP BY
:
查询:
SELECT id,
MIN( date_measured ) AS date_measured_start,
MAX( date_measured ) AS date_measured_end,
COUNT( * ) AS consecutive_out_of_range
FROM (
SELECT t.*,
ROW_NUMBER() OVER ( PARTITION BY id ORDER BY date_measured )
- ROW_NUMBER() OVER ( PARTITION BY id, out_of_range ORDER BY date_measured )
AS rn
FROM table_name t
)
WHERE out_of_range = 1
GROUP BY id, rn
输出:
ID | DATE_MEASURED_START | DATE_MEASURED_END | CONSECUTIVE_OUT_OF_RANGE ---: | :------------------ | :------------------ | -----------------------: 3147 | 2019-08-09 20:00:00 | 2019-08-09 20:00:00 | 1 3147 | 2019-08-09 23:00:00 | 2019-08-10 01:00:00 | 3 3125 | 2019-08-10 00:00:00 | 2019-08-10 02:00:00 | 3 3125 | 2019-08-09 21:00:00 | 2019-08-09 22:00:00 | 2
db <>提琴here
答案 1 :(得分:1)
这里是与MT0答案中相同方法的不同应用。该方法称为“固定差异”方法(两种解决方案中的“固定差异”都是将数据分组的附加计算值);也称为“塔比托聚糖”方法。
在此解决方案中,我直接从日期中减去了row_number()
(经过适当修改),但之后仅选择了标志等于1的行。如果您有大量的数据,但只有相对较小的行标记等于1。这是因为row_number()
需要对数据进行排序,而排序是一项昂贵的操作。为了解决这个问题,我们不需要对标志为0的行(按日期)进行排序-只需对标志为1的行进行排序即可。
编辑(基于此答案下方MT0的评论)
MT0正确地指出,我的解决方案假设在OP发布的测试数据中是正确的,但没有明确说明。即,date_measured
列中的日期时间是连续的日期时间序列,间隔为一小时。
实际上,我的解决方案真正做到的是这个。假设从一开始数据就仅由超出范围的行(标志等于1)组成,并且date_measured
列中的日期时间总是四舍五入,因为它们是在OP的测试数据中。那么,问题将是确定时间“连续”(意味着相隔一小时)的行的顺序。这就是查询的作用。
END EDIT
我使用了MT0的表格-来自他的db提琴测试。谢谢MT0!
with
tabibitosan (id, date_measured, grp) as (
select id, date_measured,
date_measured
- row_number() over (partition by id order by date_measured)
* interval '1' hour
from table_name
where out_of_range = 1
)
select id, min(date_measured) as date_measured_start,
max(date_measured) as date_measured_end,
count(*) as consecutive_out_of_range
from tabibitosan
group by id, grp
order by id, date_measured_start -- or whatever
;
ID DATE_MEASURED_START DATE_MEASURED_END CONSECUTIVE_OUT_OF_RANGE
---- ------------------- ----------------- ------------------------
3125 2019-08-09 21:00 2019-08-09 22:00 2
3125 2019-08-10 00:00 2019-08-10 02:00 3
3147 2019-08-09 20:00 2019-08-09 20:00 1
3147 2019-08-09 23:00 2019-08-10 01:00 3