簡體   English   中英

將 LEAD 與條件(?)一起使用 - 雪花

[英]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 方面的專家(恰恰相反),所以我將不勝感激!!

我可以想到兩種可能的方法,第二種可能是最好的,但我會讓你決定:

1 - 刪除額外記錄

根據您的問題,假設一個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並刪除具有相同IDAFFECTED_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

2 - 使用 FIRST_VALUE 而不是 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM