如何在Snowflake中按条件获取首个失败事件时间戳?
问题:Snowflake中计算错误解决时长的SQL实现
我在Snowflake中有一张events表,每行对应一个事件。status字段取值为failed(表示错误)或completed(表示错误已解决)。需要为每个customer_id找到两个时间戳:resolved_at和first_failed_at,最终计算这两个时间戳之间的错误解决时长(天数)。
我已经通过如下SQL获取了resolved_at,但不知道如何获取first_failed_at。需要注意的是,可能存在连续多个失败事件的情况。
with windowed as ( select *, lag(status) over (partition by customer_id order by created_at) as prev_status, lead(status) over (partition by customer_id order by created_at) as next_status, lead(created_at) over (partition by customer_id order by created_at) as next_created_at from events ) select event_id, customer_id, status, created_at, case when status = 'failed' and next_status = 'completed' then next_created_at else null end as resolved_at from windowed
示例数据及期望结果
| event_id | customer_id | status | created_at | resolved_at | first_failed_at |
|---|---|---|---|---|---|
| e7921c9c-aa8f-49bb-83fa-31fa37d629ee | a | completed | 2024-01-16 16:23:31 | ||
| 1d6a5bdf-e254-441d-9574-821844dc6c05 | a | failed | 2024-01-17 19:17:33 | 2024-01-17 19:17:33 | |
| 1a06783a-2ca4-4f68-80f0-7e7c84ac57fd | a | failed | 2024-01-22 18:07:54 | 2024-01-17 19:17:33 | |
| 61ecc8da-1e7f-4b19-92f1-fda93661a3af | a | failed | 2024-01-23 17:54:12 | 2024-01-24 19:34:48 | 2024-01-17 19:17:33 |
| 068e4220-bd57-410a-85f3-1fbb18f86053 | a | completed | 2024-01-24 19:34:48 | ||
| 4ad30d16-ff13-48f3-b315-8a588d869c08 | a | completed | 2024-01-25 17:30:54 | ||
| 82f8c57a-0e80-4ea6-96ad-3b578e5ec68a | a | failed | 2024-01-27 17:45:11 | 2024-01-27 17:45:11 | |
| 17d736d6-40de-4440-a220-e4a0612acb71 | a | failed | 2024-01-28 17:51:15 | 2024-01-29 17:23:31 | 2024-01-27 17:45:11 |
| be657472-69d0-4cd5-82e0-b33434ff213a | a | completed | 2024-01-29 17:23:31 | ||
| 3bce3901-2639-489d-8280-04518a253b33 | b | failed | 2023-12-15 15:06:32 | 2023-12-15 15:06:32 | |
| 99b1bd8b-0ac3-4868-b687-73fc93ef9ddf | b | failed | 2023-12-16 15:07:21 | 2023-12-17 15:10:42 | 2023-12-15 15:06:32 |
| 406b42e6-63c6-402e-9519-87240d007315 | b | completed | 2023-12-17 15:10:42 | ||
| 946e4c25-db5a-44e4-8c29-d7144ff5b03c | c | failed | 2023-12-15 15:06:10 | 2023-12-15 15:06:10 | |
| ba0c96d2-4b68-43e5-99a6-0f30530dd1ad | c | failed | 2023-12-16 15:06:32 | 2023-12-15 15:06:10 |
解决方案
要获取first_failed_at,核心是识别每个连续失败事件组的起始点,然后为组内所有失败事件关联该组的第一个时间戳。可以通过以下SQL实现:
with event_groups as ( select *, -- 标记每个失败组的起始:当前是failed且上一个不是failed(或上一个不存在) case when status = 'failed' and (lag(status) over (partition by customer_id order by created_at) != 'failed' or lag(status) over (partition by customer_id order by created_at) is null) then 1 else 0 end as is_failure_start, lead(status) over (partition by customer_id order by created_at) as next_status, lead(created_at) over (partition by customer_id order by created_at) as next_created_at from events ), grouped_failures as ( select *, -- 累积求和,为每个连续失败组分配唯一ID sum(is_failure_start) over (partition by customer_id order by created_at rows between unbounded preceding and current row) as failure_group_id from event_groups ), failure_group_starts as ( select customer_id, failure_group_id, min(created_at) as first_failed_at from grouped_failures where status = 'failed' group by customer_id, failure_group_id ) select g.event_id, g.customer_id, g.status, g.created_at, -- 保留原有的resolved_at逻辑 case when g.status = 'failed' and g.next_status = 'completed' then g.next_created_at else null end as resolved_at, -- 关联对应失败组的起始时间 fs.first_failed_at from grouped_failures g left join failure_group_starts fs on g.customer_id = fs.customer_id and g.failure_group_id = fs.failure_group_id order by g.customer_id, g.created_at;
逻辑说明
- 标记失败组起始:用
lag()函数判断当前失败事件是否是连续失败组的第一个,标记为is_failure_start。 - 生成失败组ID:通过累积求和
is_failure_start,为每个连续失败事件组分配唯一的failure_group_id。 - 获取组内最早时间:按
customer_id和failure_group_id分组,取每组的最小created_at作为first_failed_at。 - 关联结果:将分组后的起始时间关联回原事件,同时保留原有的
resolved_at计算逻辑。
内容的提问来源于stack exchange,提问作者kimi
相关产品推荐
相关产品推荐

