表设备_状态
身份证 | 状态 | 时间戳 |
---|---|---|
1 | 活跃 | 2023-01-13T18.00.01.0187528 |
2 | 活跃 | 2023-01-13T18.00.01.0187529 |
1 | 失败 | 2023-01-13T18.00.01.0187530 |
3 | 活跃 | 2023-01-13T18.00.01.0187531 |
1 | 失败 | 2023-01-13T18.00.01.0187532 |
1 | 活跃 | 2023-01-13T18.00.01.0187533 |
3 | 活跃 | 2023-01-13T18.00.01.0187534 |
1 | 失败 | 2023-01-13T18.00.01.0187535 |
4 | 失败 | 2023-01-13T18.00.01.0187536 |
1 | 活跃 | 2023-01-13T18.00.01.0187537 |
预期输出(需要SQL查询生成):
身份证 | 失败_开始 | 失败_结束 |
---|---|---|
1 | 2023-01-13T18.00.01.0187530 | 2023-01-13T18.00.01.0187532 |
1 | 2023-01-13T18.00.01.0187535 | 2023-01-13T18.00.01.0187535 |
4 | 2023-01-13T18.00.01.0187536 | 2023-01-13T18.00.01.0187536 |
基本上,对于每个 ID 获取最小值(时间戳)和最大值(时间戳),但超过该 ID 的连续记录。如果只有一条记录,则与样本结果集中的第二条和第三条记录一样,min=max。
我已经尝试过这个(及其各种子查询变体)
SELECT Id, min(Timestamp) AS Fail_Begin, max(Timestamp) AS Fail_End
FROM Device_Status
GROUP BY Id
但需要仅按连续出现的记录进行分组,
那么也许首先添加状态更改的概念会有帮助?从零开始作为初始状态,然后每次下一条记录不同时将更改代码增加 1,以便生成像这样的中间结果...
表Device_Status_With_Change_Column
身份证 | 状态 | 改变 | 时间戳 |
---|---|---|---|
1 | 活跃 | 0 | 2023-01-13T18.00.01.0187528 |
2 | 活跃 | 0 | 2023-01-13T18.00.01.0187529 |
1 | 失败 | 1 | 2023-01-13T18.00.01.0187530 |
3 | 活跃 | 0 | 2023-01-13T18.00.01.0187531 |
1 | 失败 | 1 | 2023-01-13T18.00.01.0187532 |
1 | 活跃 | 2 | 2023-01-13T18.00.01.0187533 |
3 | 活跃 | 0 | 2023-01-13T18.00.01.0187534 |
1 | 失败 | 3 | 2023-01-13T18.00.01.0187535 |
4 | 失败 | 0 | 2023-01-13T18.00.01.0187536 |
1 | 活跃 | 4 | 2023-01-13T18.00.01.0187537 |
然后做
SELECT Id, Change, min(Timestamp) AS Fail_Begin, max(Timestamp) AS Fail_End
FROM Device_Status_With_Change_Column
GROUP BY Id, Change
除了用编程语言循环结果集之外,我还没有看到直接的 SQL 语句可以在没有中间表的情况下一次性完成此操作,并且我没有看到如何计算列 Change(在 SQL 中)。
下面的解决方案首先使用子查询将行号与每个
failed
记录关联起来,该行号由每个id
块进行分区。然后,子查询被 join
变为自身:
with cte as (
select row_number() over (partition by t.id order by t.timestamp) r, t.* from device_status t where t.status = 'Failed'
)
select c.id, c.timestamp fail_start, coalesce(c1.timestamp, c.timestamp) fail_end
from cte c join cte c1 on c.id = c1.id and c.r + 1 = c1.r
where c.r % 2 = 1