[英]Using LEAD with condition(?) - snowflake
我有 +10k 個 ID,每個 ID 都有 10 個區域; 每個區域都會以某種方式受到影響
我想計算每個 ID 每個區域受到影響的持續時間,按天排序(考慮整個上周)
要知道某個區域是否/何時受到影響, AFFECTED_ZONE
列將返回 1 到 10 之間的值(確定哪個區域是那個區域)
我知道一旦AFFECTED_ZONE
中的下一行為 0,區域就會被標准化
因此,例如,它看起來有點像這樣:
日期 | ID | AFFECTED_ZONE |
---|---|---|
2022-12-21 15:00:00 | 1個 | 1個 |
2022-12-21 15:03:00 | 1個 | 0 |
2022-12-21 15:15:00 | 1個 | 3個 |
2022-12-21 15:25:00 | 1個 | 0 |
2022-12-21 16:00:00 | 1個 | 0 |
2022-12-21 16:43:00 | 1個 | 4個 |
2022-12-21 17:00:00 | 1個 | 0 |
在這種情況下,來自 ID 1 的區域1在 15:00:00 受到影響,並在 15:03:00 歸一化 - 總體影響時間應為3 分鍾; 與此示例中的區域4相同(在 16:43:00 受到影響並在 17:00:00 歸一化 - 總體影響時間應為17 分鍾)
對於 zone 3 ,影響發生在 15:15:00,並在 15:25:00(第一個 0)歸一化,我們在我們不考慮的后期時間有另一個 0 - 總體影響時間應該是10 分鍾
問題是,有時它看起來像這樣:
日期 | ID | AFFECTED_ZONE |
---|---|---|
2022-12-21 15:00:00 | 1個 | 1個 |
2022-12-21 15:03:00 | 1個 | 1個 |
2022-12-21 15:15:00 | 1個 | 0 |
2022-12-21 15:25:00 | 1個 | 6個 |
2022-12-21 16:00:00 | 1個 | 4個 |
2022-12-21 16:43:00 | 1個 | 3個 |
2022-12-21 17:00:00 | 1個 | 0 |
在這種情況下,來自 ID 1 的區域1在 15:00:00 受到影響並在 15:15:00 歸一化,但是 1 在 15:03:00 再次出現,但應該取消考慮,因為同一個區域自 15:00:00 以來已經受到影響 - 總體影響時間應為15 分鍾
此后, 6區、 4區、 3區連續受到影響,17:00:00才恢復正常; 每個區域的總受影響時間分別應為95 分鍾、 60 分鍾和17 分鍾
我無法弄清楚第二部分。 起初,我像這樣分開每個事件的日期(影響和規范化):
case when affectation_zone <> 0 then date end as affected_at,
case when affectation_zone = 0 then date end as normal_at
然后,我添加了一個 LEAD() function,這樣我就可以從NORMAL_AT
日期中減去AFFECTED_AT
日期,從而找到總體受影響的時間,如下所示:
datediff(minutes, affected_at, lead(normal_at) over (partition by id order by date)) as lead
它適用於第一種情況
日期 | ID | AFFECTED_ZONE | 影響_AT | 正常_AT | 帶領 |
---|---|---|---|---|---|
2022-12-21 15:00:00 | 1個 | 1個 | 2022-12-21 15:00:00 | null | 3個 |
2022-12-21 15:03:00 | 1個 | 0 | null | 2022-12-21 15:03:00 | null |
2022-12-21 15:15:00 | 1個 | 3個 | 2022-1-21 15:15:00 | null | 10 |
2022-12-21 15:25:00 | 1個 | 0 | null | 2022-12-21 15:25:00 | null |
2022-12-21 16:00:00 | 1個 | 0 | null | 2022-12-21 16:00:00 | null |
2022-12-21 16:43:00 | 1個 | 4個 | 2022-12-21 16:43:00 | null | 17 |
2022-12-21 17:00:00 | 1個 | 0 | null | 2022-12-21 17:00:00 | null |
但是,對於第二個,LEAD() 僅考慮AFFECTED_AT
列不是 null 的最后一行,不考慮其他行,如下所示:
日期 | ID | AFFECTED_ZONE | 影響_AT | 正常_AT | 帶領 |
---|---|---|---|---|---|
2022-12-21 15:00:00 | 1個 | 1個 | 2022-12-21 15:00:00 | null | null |
2022-12-21 15:03:00 | 1個 | 1個 | 2022-12-21 15:03:00 | null | 12 |
2022-12-21 15:15:00 | 1個 | 0 | null | 2022-12-21 15:15:00 | null |
2022-12-21 15:25:00 | 1個 | 6個 | 2022-12-21 15:25:00 | null | null |
2022-12-21 16:00:00 | 1個 | 4個 | 2022-12-21 16:00:00 | null | null |
2022-12-21 16:43:00 | 1個 | 3個 | 2022-12-21 16:43:00 | null | 17 |
2022-12-21 17:00:00 | 1個 | 0 | null | 2022-12-21 17:00:00 | null |
我可以使用 LEAD() function 忽略空值,它適用於一個接一個地有不同區域的情況,但在同一個區域重復出現的情況下它不起作用,就像我一樣增加不必要的時間,例如:
日期 | ID | AFFECTED_ZONE | 影響_AT | 正常_AT | 帶領 |
---|---|---|---|---|---|
2022-12-21 15:00:00 | 1個 | 1個 | 2022-12-21 15:00:00 | null | 15 |
2022-12-21 15:03:00 | 1個 | 1個 | 2022-12-21 15:03:00 | null | 12 |
2022-12-21 15:15:00 | 1個 | 0 | null | 2022-12-21 15:15:00 | null |
2022-12-21 15:25:00 | 1個 | 6個 | 2022-12-21 15:25:00 | null | 95 |
2022-12-21 16:00:00 | 1個 | 4個 | 2022-12-21 16:00:00 | null | 60 |
2022-12-21 16:43:00 | 1個 | 3個 | 2022-12-21 16:43:00 | null | 17 |
2022-12-21 17:00:00 | 1個 | 0 | null | 2022-12-21 17:00:00 | null |
區域1的整體情感時間應該是 15 分鍾,但如果我把所有東西都加起來,那就是 23 分鍾
關於如何解決這個問題的任何想法? 我不是 Snowflake/SQL 方面的專家(恰恰相反),所以我將不勝感激!!
我可以想到兩種可能的方法,第二種可能是最好的,但我會讓你決定:
根據您的問題,假設一個ID
只能影響AFFECTED_ZONE
一次(每次出現可能包括多條記錄)。 IE
日期 | ID | AFFECTED_ZONE |
---|---|---|
2022-12-21 15:00:00 | 1個 | 1個 |
2022-12-21 15:03:00 | 1個 | 1個 |
2022-12-21 15:15:00 | 1個 | 0 |
2022-12-21 15:25:00 | 1個 | 6個 |
2022-12-21 16:00:00 | 1個 | 4個 |
2022-12-21 16:43:00 | 1個 | 3個 |
2022-12-21 17:00:00 | 1個 | 0 |
並不是
日期 | ID | AFFECTED_ZONE |
---|---|---|
2022-12-21 15:00:00 | 1個 | 1個 |
2022-12-21 15:03:00 | 1個 | 1個 |
2022-12-21 15:15:00 | 1個 | 0 |
2022-12-21 15:25:00 | 1個 | 1個 |
2022-12-21 16:00:00 | 1個 | 0 |
2022-12-21 16:43:00 | 1個 | 3個 |
2022-12-21 17:00:00 | 1個 | 0 |
我們可以使用LAG
function 來查找之前的每條記錄AFFECTED_ZONE
並刪除具有相同ID
和AFFECTED_ZONE
的記錄 - 同時忽略where AFFECTED_ZONE = 0
的位置。 如果您確實多次出現ID, AFFECTED_ZONE
配對,此過程會將它們合並在一起。
select foo.id,
foo.date,
foo.affected_zone
from (select id,
date,
affected_zone,
lag(affected_zone,1) over (partition by id
order by date) prev_affected_zone
from your_table) foo
where ifnull(foo.affected_zone,-1) != ifnull(foo.prev_affected_zone,-1)
or ifnull(foo.affected_zone,-1) = 0
這種方法會給你類似的東西
日期 | ID | AFFECTED_ZONE |
---|---|---|
2022-12-21 15:00:00 | 1個 | 1個 |
2022-12-21 15:15:00 | 1個 | 0 |
2022-12-21 15:25:00 | 1個 | 6個 |
2022-12-21 16:00:00 | 1個 | 4個 |
2022-12-21 16:43:00 | 1個 | 3個 |
2022-12-21 17:00:00 | 1個 | 0 |
允許您使用現有的LEAD
使用您當前的流程,但將LEAD
替換為FIRST_VALUE
。
FIRST_VALUE
將 select 作為有序值組中的第一個值,因此我們可以忽略空值並返回當前行之后的第一個normal_at
值。
select date,
id,
affected_zone,
affected_at,
first_value(normal_at ignore nulls) over (partition by id
order by date
rows between current row and unbound following) normal_at
from (select id,
date,
affected_zone,
case when affected_zone != 0 then date end affected_at,
case when affected_zone = 0 then date end normal_at
from your_table) foo
這應該給你:
日期 | ID | AFFECTED_ZONE | 影響_AT | 正常_AT |
---|---|---|---|---|
2022-12-21 15:00:00 | 1個 | 1個 | 2022-12-21 15:00:00 | 2022-12-21 15:15:00 |
2022-12-21 15:03:00 | 1個 | 1個 | 2022-12-21 15:03:00 | 2022-12-21 15:15:00 |
2022-12-21 15:15:00 | 1個 | 0 | null | 2022-12-21 15:15:00 |
2022-12-21 15:25:00 | 1個 | 6個 | 2022-12-21 15:25:00 | null |
2022-12-21 16:00:00 | 1個 | 4個 | 2022-12-21 16:00:00 | null |
2022-12-21 16:43:00 | 1個 | 3個 | 2022-12-21 16:43:00 | 2022-12-21 17:00:00 |
2022-12-21 17:00:00 | 1個 | 0 | null | 2022-12-21 17:00:00 |
然后,您可以計算持續時間和 select 每個ID, AFFECTED_ZONE
的第一條記錄,AFFECTED_ZONE 配對,可能帶有ROW_NUMBER
。
我會將其視為一個間隙和孤島問題,這為我們提供了很大的靈活性來解決各種用例。
我的選擇是使用 window 函數定義一組相鄰記錄,這些記錄以一個或多個受影響的區域開始並以規范化 ( affected_zone = 0
) 結束:
select t.*,
sum(case when lag_affected_zone = 0 then 1 else 0 end) over(partition by id order by date) grp
from (
select t.*,
lag(affected_zone, 1, 0) over(partition by id order by date) lag_affected_zone
from mytable t
) t
從您提供的一些數據的混合開始,我希望代表不同的用例,這將返回:
日期 | ID | AFFECTED_ZONE | 滯后影響區 | 組 |
---|---|---|---|---|
2022-12-21 15:00:00.000 | 1個 | 1個 | 0 | 1個 |
2022-12-21 15:03:00.000 | 1個 | 1個 | 1個 | 1個 |
2022-12-21 15:15:00.000 | 1個 | 0 | 1個 | 1個 |
2022-12-21 15:17:00.000 | 1個 | 0 | 0 | 2個 |
2022-12-21 15:25:00.000 | 1個 | 6個 | 0 | 3個 |
2022-12-21 16:00:00.000 | 1個 | 4個 | 6個 | 3個 |
2022-12-21 16:43:00.000 | 1個 | 3個 | 4個 | 3個 |
2022-12-21 16:50:00.000 | 1個 | 1個 | 3個 | 3個 |
2022-12-21 17:00:00.000 | 1個 | 0 | 1個 | 3個 |
您可以看到記錄是如何組合在一起形成一致孤島的。 現在我們可以對每個組進行處理:我們想要將每個受影響區域的最早日期帶入組中,並將其與該組的最新日期進行比較(對應於規范化步驟); 我們可以使用聚合:
select *
from (
select id, affected_zone, min(date) affected_at, max(max(date)) over(partition by grp) normalized_at
from (
select t.*,
sum(case when lag_affected_zone = 0 then 1 else 0 end) over(partition by id order by date) grp
from (
select t.*,
lag(affected_zone, 1, 0) over(partition by id order by date) lag_affected_zone
from mytable t
) t
) t
group by id, affected_zone, grp
) t
where affected_zone != 0
order by id, affected_at
ID | 受影響的區域 | 受影響的 | 標准化_at |
---|---|---|---|
1個 | 1個 | 2022-12-21 15:00:00.000 | 2022-12-21 15:15:00.000 |
1個 | 6個 | 2022-12-21 15:25:00.000 | 2022-12-21 17:00:00.000 |
1個 | 4個 | 2022-12-21 16:00:00.000 | 2022-12-21 17:00:00.000 |
1個 | 3個 | 2022-12-21 16:43:00.000 | 2022-12-21 17:00:00.000 |
1個 | 1個 | 2022-12-21 16:50:00.000 | 2022-12-21 17:00:00.000 |
這是DB Fiddle 上的演示:這是 SQL 服務器,但使用 Snowflakes 也支持的標准 SQL。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.