根據述詞掃描數據、比對和建置序列。
比對記錄是根據運算符步驟中定義的述詞來決定。 述詞可以取決於先前步驟所產生的狀態。 比對記錄的輸出取決於運算符步驟中定義的輸入記錄和指派。
Syntax
T| scan [ with_match_id=MatchIdColumnName ] [ declare(ColumnDeclarations) ] with(StepDefinitions)
ColumnDeclarations 語法
欄位名稱:欄位類型[=預設值 ] [, ... ]
StepDefinition 語法
step
步驟名稱 [ output | last = all | none]: 條件 [=> 欄位=分配 [, ... ] ];
深入瞭解 語法慣例。
Parameters
| Name | 類型 | Required | Description |
|---|---|---|---|
| T | string |
✔️ | 輸入表格式來源。 |
| MatchIdColumnName | string |
在掃描執行時附加至輸出之類型的數據 long 行名稱。 指出記錄相符專案的 0 型索引。 |
|
| ColumnDeclarations | string |
宣告對 T 的結構進行擴展。這些欄位會在步驟中被賦予數值。 若未被指派,則會回傳 DefaultValue 。 除非另有說明, DefaultValue 為 null。 |
|
| StepName | string |
✔️ | 用來參考掃描條件和指派狀態中的值。 步驟名稱必須是唯一的。 |
| Condition | string |
✔️ | 評估為 true 或 false 的表示式,定義輸入中的哪些記錄符合步驟。 當條件為 true 步驟狀態或上一個步驟的狀態時,記錄會比對步驟。 |
| Assignment | string |
當記錄符合步驟時,指派給對應數據行的純量表達式。 | |
output |
string |
在重複的相符專案上控制步驟的輸出邏輯。
all 輸出符合步驟的所有記錄、 last 只輸出步驟一系列重複比對中的最後一筆記錄,而且 none 不會輸出符合步驟的記錄。 預設值為 all。 |
Returns
從輸入到步驟之記錄的每個相符項目記錄。 輸出的架構是以 子句中的數據 declare 行擴充的來源架構。
掃描邏輯
scan 會逐筆記錄逐一串行化輸入數據,比較每個記錄與每個步驟的條件,同時考慮到每個步驟的目前狀態。
State
運算子的基礎狀態 scan 可以視為具有每個 step數據列的數據表。 每個步驟都會使用數據行的最新值,以及先前所有步驟和目前步驟中宣告的變數,維護自己的狀態。 如果相關,它也會保留進行中序列的相符標識符。
若掃描運算子有 n 個步驟,分別名為 s_1、 s_2、... s_n ,則第 s_k 步的狀態下會有 k 條記錄,分別對應於 s_1、 s_2、...、 s_k。
繼姓。ColumnName 格式用於在狀態中引用值。 例如,s_2.col1會參考屬於 s_k 狀態中步驟 s_2 的欄位col1。 如需詳細範例,請參閱 掃描邏輯逐步解說。
每當掃描的輸入記錄符合步驟時,狀態就會啟動空白並更新。 當當前步驟的狀態非空時,該步驟稱為具有 主動序列。
配對邏輯
每個輸入記錄都會根據從最後一個步驟到第一個步驟的所有反向順序進行評估。 當記錄 r 與某個步驟 s_k 進行評估時,會應用以下邏輯:
檢查一: 若前一步(s_k-1)的狀態非空,且 r 符合 條件s_k,則會發生匹配。 比對會導致下列動作:
- s_k狀態已解除。
- s_k-1 狀態被提升為 State of s_k。
- s_k的賦值會被計算並擴展 r。
- 擴展的 r 會加到輸出和 s_k 狀態中。
Note
若 檢定 1成功配對,檢 定2 將被忽略, r 將與 s_k-1進行評估。
檢查2: 如果 s_k 狀態有主動序列或 s_k 是第一步,且 r 符合 條件s_k,則會發生匹配。 比對會導致下列動作:
- s_k的賦值會被計算並擴展 r。
- 代表 s_k 處於 s_k 狀態的值會被擴展 r 的值取代。
- 若 s_k 定義為
output=all,則將擴展 的 r 加入輸出。 - 若 s_k 為第一步,則新序列開始,匹配 ID 增加
1。 這隻會影響使用 時的with_match_id輸出。
當 s_k 檢查完成後, r 會進行與 s_k-1 的對比。
如需此邏輯的詳細範例,請參閱 掃描邏輯逐步解說。
Examples
本節中的範例示範如何使用 語法來協助您開始使用。
本文中的範例會使用 說明叢集中公開可用的資料表,例如
StormEvents[範例] 資料庫中的資料表。
本文中的範例使用公開可用的資料表,例如
WeatherWeather analytics 範例資源庫中的資料表。 您可能需要修改範例查詢中的資料表名稱,以符合工作區中的資料表。
累積總和
計算輸入數據行的累計總和。 此範例的結果等同於使用 row_cumsum()。
range x from 1 to 5 step 1
| scan declare (cumulative_x:long=0) with
(
step s1: true => cumulative_x = x + s1.cumulative_x;
)
Output
| x | cumulative_x |
|---|---|
| 1 | 1 |
| 2 | 3 |
| 3 | 6 |
| 4 | 10 |
| 5 | 15 |
具有重設條件之多個數據行的累計總和
計算兩個輸入數據行的累計總和,每當累計總和達到10個以上時,將總和重設為當前記錄值。
range x from 1 to 5 step 1
| extend y = 2 * x
| scan declare (cumulative_x:long=0, cumulative_y:long=0) with
(
step s1: true => cumulative_x = iff(s1.cumulative_x >= 10, x, x + s1.cumulative_x),
cumulative_y = iff(s1.cumulative_y >= 10, y, y + s1.cumulative_y);
)
Output
| x | y | cumulative_x | cumulative_y |
|---|---|---|---|
| 1 | 2 | 1 | 2 |
| 2 | 4 | 3 | 6 |
| 3 | 6 | 6 | 12 |
| 4 | 8 | 10 | 8 |
| 5 | 10 | 5 | 18 |
向前填入數據行
向前填入字串數據行。 每個空白值都會指派最後一個未見空值。
let Events = datatable (Ts: timespan, Event: string) [
0m, "A",
1m, "",
2m, "B",
3m, "",
4m, "",
6m, "C",
8m, "",
11m, "D",
12m, ""
]
;
Events
| sort by Ts asc
| scan declare (Event_filled: string="") with
(
step s1: true => Event_filled = iff(isempty(Event), s1.Event_filled, Event);
)
Output
| Ts | Event | Event_filled |
|---|---|---|
| 00:00:00 | A | A |
| 00:01:00 | A | |
| 00:02:00 | B | B |
| 00:03:00 | B | |
| 00:04:00 | B | |
| 00:06:00 | C | C |
| 00:08:00 | C | |
| 00:11:00 | D | D |
| 00:12:00 | D |
會話標籤
將輸入分割成會話:會話會在會話的第一個事件之後 30 分鐘結束,之後就會啟動新的會話。 請注意 flag 的 with_match_id 使用,該標記為每個不同的 掃描匹配(會話)分配唯一值。 另外請注意,這個例子特別使用了兩個 步驟 ,條件 inSession 是 為 , true 因此它捕捉並輸出所有輸入紀錄,同時 endSession 擷取距離 sessionStart 目前匹配值超過 30 公尺的紀錄。 步驟 endSession 表示 output=none 它不會產生輸出記錄。 步驟 endSession 是用來將目前相符專案的狀態從 inSession 前進到 endSession,讓新的比對 (session) 從目前的記錄開始。
let Events = datatable (Ts: timespan, Event: string) [
0m, "A",
1m, "A",
2m, "B",
3m, "D",
32m, "B",
36m, "C",
38m, "D",
41m, "E",
75m, "A"
]
;
Events
| sort by Ts asc
| scan with_match_id=session_id declare (sessionStart: timespan) with
(
step inSession: true => sessionStart = iff(isnull(inSession.sessionStart), Ts, inSession.sessionStart);
step endSession output=none: Ts - inSession.sessionStart > 30m;
)
Output
| Ts | Event | sessionStart | session_id |
|---|---|---|---|
| 00:00:00 | A | 00:00:00 | 0 |
| 00:01:00 | A | 00:00:00 | 0 |
| 00:02:00 | B | 00:00:00 | 0 |
| 00:03:00 | D | 00:00:00 | 0 |
| 00:32:00 | B | 00:32:00 | 1 |
| 00:36:00 | C | 00:32:00 | 1 |
| 00:38:00 | D | 00:32:00 | 1 |
| 00:41:00 | E | 00:32:00 | 1 |
| 01:15:00 | A | 01:15:00 | 2 |
計算每個使用者的會話長度
使用 scan 運算子計算每個使用者會話的會話開始時間、結束時間和持續時間。 會話定義為使用者登入與後續註銷之間的期間。 藉由結合 partition 和 和 scanoutput=noneoutput=all,此模式可確保每個會話會傳回單一數據列(也就是每個登入/註銷配對),而不是每個事件的數據列。
邏輯的運作方式如下:
- 在步驟 s1:使用掃描步驟搭配 擷取登入時間戳
output=none - 在步驟 s2:只有在找到相符的註銷時,才發出數據列
output=all
let LogsEvents = datatable(Timestamp:datetime, userID:int, EventType:string)
[
datetime(2024-05-28 08:15:23), 1, "login",
datetime(2024-05-28 08:30:15), 2, "login",
datetime(2024-05-28 09:10:27), 3, "login",
datetime(2024-05-28 12:30:45), 1, "logout",
datetime(2024-05-28 11:45:32), 2, "logout",
datetime(2024-05-28 13:25:19), 3, "logout"
];
LogsEvents
| sort by userID, Timestamp
| partition hint.strategy=native by userID (
sort by Timestamp asc
| scan declare (start: datetime, end: datetime, sessionDuration: timespan) with (
step s1 output=none: EventType == "login" => start = Timestamp;
step s2 output=all: EventType == "logout" => start = s1.start, end = Timestamp, sessionDuration = Timestamp - s1.start;
)
)
| project start, end, userID, sessionDuration
Output
| userID | start | end | sessionDuration |
|---|---|---|---|
| 1 | 2024-05-28 08:15:23.0000 | 2024-05-28 12:30:45.0000 | 04:15:22 |
| 3 | 2024-05-28 09:10:27.0000 | 2024-05-28 13:25:19.0000 | 04:14:52 |
| 2 | 2024-05-28 08:30:15.0000 | 2024-05-28 11:45:32.0000 | 03:15:17 |
開始和停止之間的事件
尋找事件 Start 與 5 分鐘內發生之事件之間的所有事件 Stop 序列。 為每個序列指派相符標識碼。
let Events = datatable (Ts: timespan, Event: string) [
0m, "A",
1m, "Start",
2m, "B",
3m, "D",
4m, "Stop",
6m, "C",
8m, "Start",
11m, "E",
12m, "Stop"
]
;
Events
| sort by Ts asc
| scan with_match_id=m_id with
(
step s1: Event == "Start";
step s2: Event != "Start" and Event != "Stop" and Ts - s1.Ts <= 5m;
step s3: Event == "Stop" and Ts - s1.Ts <= 5m;
)
Output
| Ts | Event | m_id |
|---|---|---|
| 00:01:00 | Start | 0 |
| 00:02:00 | B | 0 |
| 00:03:00 | D | 0 |
| 00:04:00 | Stop | 0 |
| 00:08:00 | Start | 1 |
| 00:11:00 | E | 1 |
| 00:12:00 | Stop | 1 |
計算事件的自定義漏鬥圖
計算序列Hail的漏鬥完成 ->Tornado>Thunderstorm Wind 依據State事件之間的時間自定義臨界值(Tornado在 1h 內和Thunderstorm Wind內)。2h 此範例與 funnel_sequence_completion 外掛相似,但允許更大彈性。
StormEvents
| partition hint.strategy=native by State
(
sort by StartTime asc
| scan with
(
step hail: EventType == "Hail";
step tornado: EventType == "Tornado" and StartTime - hail.StartTime <= 1h;
step thunderstormWind: EventType == "Thunderstorm Wind" and StartTime - tornado.StartTime <= 2h;
)
)
| summarize dcount(State) by EventType
Output
| EventType | dcount_State |
|---|---|
| Hail | 50 |
| Tornado | 34 |
| 雷暴風 | 32 |
掃描邏輯逐步解說
let Events = datatable (Ts: timespan, Event: string) [
0m, "A",
1m, "Start",
2m, "B",
3m, "D",
4m, "Stop",
6m, "C",
8m, "Start",
11m, "E",
12m, "Stop"
]
;
Events
| sort by Ts asc
| scan with_match_id=m_id with
(
step s1: Event == "Start";
step s2: Event != "Start" and Event != "Stop" and Ts - s1.Ts <= 5m;
step s3: Event == "Stop" and Ts - s1.Ts <= 5m;
)
Output
| Ts | Event | m_id |
|---|---|---|
| 00:01:00 | Start | 0 |
| 00:02:00 | B | 0 |
| 00:03:00 | D | 0 |
| 00:04:00 | Stop | 0 |
| 00:08:00 | Start | 1 |
| 00:11:00 | E | 1 |
| 00:12:00 | Stop | 1 |
州內情況
請將 運算符的狀態視為具有每個步驟數據列的 scan 數據表,其中每個步驟都有自己的狀態。 此狀態包含所有先前步驟和目前步驟中數據行和宣告變數的最新值。 欲了解更多,請參閱州。
在此範例中,狀態可以使用下表來表示:
| 步驟 | m_id | s1.Ts | s1.Event | s2.Ts | s2.Event | s3.Ts | s3.Event |
|---|---|---|---|---|---|---|---|
| s1 | X | X | X | X | |||
| s2 | X | X | |||||
| s3 |
“X” 表示特定欄位與此步驟無關。
比對邏輯
本節依照每筆記錄Events的匹配邏輯,說明每步狀態與輸出的轉換。
Note
輸入記錄會根據從最後一個步驟() 到第一個步驟的s3反向順序來評估步驟。s1
紀錄 1
| Ts | Event |
|---|---|
| 0m | "A" |
記錄每個步驟的評估:
-
s3:檢查1未通過是因為的s2狀態為空,檢查2未通過是因為s3缺少活躍序列。 -
s2:檢查1未通過是因為的s1狀態為空,檢查2未通過是因為s2缺少活躍序列。 -
s1: 檢查1 無關緊要,因為沒有前一步驟。 檢查2未通過,因為紀錄未符合條件。Event == "Start"記錄 1 被丟棄時不影響狀態或輸出。
State:
| 步驟 | m_id | s1.Ts | s1.Event | s2.Ts | s2.Event | s3.Ts | s3.Event |
|---|---|---|---|---|---|---|---|
| s1 | X | X | X | X | |||
| s2 | X | X | |||||
| s3 |
紀錄 2
| Ts | Event |
|---|---|
| 1m | "Start" |
記錄每個步驟的評估:
-
s3:檢查1未通過是因為的s2狀態為空,檢查2未通過是因為s3缺少活躍序列。 -
s2:檢查1未通過是因為的s1狀態為空,檢查2未通過是因為s2缺少活躍序列。 -
s1: 檢查1 無關緊要,因為沒有前一步驟。 檢查2通過是因為記錄符合條件。Event == "Start"此比對會起始新的序列,並m_id指派 。 記錄 2 及其m_id(0) 會被加到狀態和輸出中。
State:
| 步驟 | m_id | s1.Ts | s1.Event | s2.Ts | s2.Event | s3.Ts | s3.Event |
|---|---|---|---|---|---|---|---|
| s1 | 0 | 00:01:00 | "Start" | X | X | X | X |
| s2 | X | X | |||||
| s3 |
紀錄 3
| Ts | Event |
|---|---|
| 2m | "B" |
記錄每個步驟的評估:
-
s3:檢查1未通過是因為的s2狀態為空,檢查2未通過是因為s3缺少活躍序列。 -
s2: 檢查 1 通過是因為 的s1狀態非空,且記錄符合條件Ts - s1.Ts < 5m。 此比對會導致 清除的狀態s1,並將中的s1序列升階為s2。 記錄 3 及其m_id(0)會被加到狀態和輸出中。 -
s1: 檢查1 無關,因為沒有前一步驟,檢查 2 未通過,因為紀錄不符合條件Event == "Start"。
State:
| 步驟 | m_id | s1.Ts | s1.Event | s2.Ts | s2.Event | s3.Ts | s3.Event |
|---|---|---|---|---|---|---|---|
| s1 | X | X | X | X | |||
| s2 | 0 | 00:01:00 | "Start" | 00:02:00 | "B" | X | X |
| s3 |
第四場紀錄
| Ts | Event |
|---|---|
| 3m | "D" |
記錄每個步驟的評估:
-
s3: 檢查 1未通過是因為紀錄不符合條件Event == "Stop",檢查 2 未通過是因為s3缺少活躍序列。 -
s2: 檢查 1 未通過,因為 的s1狀態為空。 它通過檢查2,因為它符合條件。Ts - s1.Ts < 5m記錄 4 及其m_id(0) 會被加入狀態和輸出。 此記錄中的值會覆寫 和s2.Ts的先前狀態值s2.Event。 -
s1: 檢查1 無關,因為沒有前一步驟,檢查 2 未通過,因為紀錄不符合條件Event == "Start"。
State:
| 步驟 | m_id | s1.Ts | s1.Event | s2.Ts | s2.Event | s3.Ts | s3.Event |
|---|---|---|---|---|---|---|---|
| s1 | X | X | X | X | |||
| s2 | 0 | 00:01:00 | "Start" | 00:03:00 | "D" | X | X |
| s3 |
紀錄 5
| Ts | Event |
|---|---|
| 4m | "Stop" |
記錄每個步驟的評估:
-
s3: 檢查 1 通過,因為s2非空且符合s3Event == "Stop"條件。 此比對會導致 清除的狀態s2,並將中的s2序列升階為s3。 記錄 5 及其m_id(0) 會加入狀態和輸出。 -
s2:檢查1未通過是因為的s1狀態為空,檢查2未通過是因為s2缺少活躍序列。 -
s1: 檢查1 無關緊要,因為沒有前一步驟。 檢查2未通過,因為紀錄未符合條件。Event == "Start"
State:
| 步驟 | m_id | s1.Ts | s1.Event | s2.Ts | s2.Event | s3.Ts | s3.Event |
|---|---|---|---|---|---|---|---|
| s1 | X | X | X | X | |||
| s2 | X | X | |||||
| s3 | 0 | 00:01:00 | "Start" | 00:03:00 | "D" | 00:04:00 | "Stop" |
紀錄 6
| Ts | Event |
|---|---|
| 6m | "C" |
記錄每個步驟的評估:
-
s3: 檢查 1未通過是因為的s2狀態為空 ,檢查2 未通過是因為s3不符合s3條件Event == "Stop"。 -
s2:檢查1未通過是因為的s1狀態為空,檢查2未通過是因為s2缺少活躍序列。 -
s1: 檢查 1 未通過是因為沒有前一步驟,檢查 2 未通過是因為它不符合條件Event == "Start"。 記錄 6 被丟棄而不影響狀態或輸出。
State:
| 步驟 | m_id | s1.Ts | s1.Event | s2.Ts | s2.Event | s3.Ts | s3.Event |
|---|---|---|---|---|---|---|---|
| s1 | X | X | X | X | |||
| s2 | X | X | |||||
| s3 | 0 | 00:01:00 | "Start" | 00:03:00 | "D" | 00:04:00 | "Stop" |
紀錄 7
| Ts | Event |
|---|---|
| 8m | "Start" |
記錄每個步驟的評估:
-
s3:檢查1未通過是因為的s2狀態為空,檢查2未通過是因為它不符合條件Event == "Stop"。 -
s2:檢查1未通過是因為的s1狀態為空,檢查2未通過是因為s2缺少活躍序列。 -
s1: 檢查 1未通過,因為沒有前一步驟。 它通過檢查2,因為它符合條件。Event == "Start"此比對會使用新的 ,在 中s1起始新的m_id序列。 記錄 7 及其m_id(1) 會被加入狀態和輸出中。
State:
| 步驟 | m_id | s1.Ts | s1.Event | s2.Ts | s2.Event | s3.Ts | s3.Event |
|---|---|---|---|---|---|---|---|
| s1 | 1 | 00:08:00 | "Start" | X | X | X | X |
| s2 | X | X | |||||
| s3 | 0 | 00:01:00 | "Start" | 00:03:00 | "D" | 00:04:00 | "Stop" |
Note
狀態中現在有兩個作用中序列。
紀錄 8
| Ts | Event |
|---|---|
| 11m | "E" |
記錄每個步驟的評估:
-
s3:檢查1未通過是因為的s2狀態為空,檢查2未通過是因為它不符合s3條件。Event == "Stop" -
s2: 檢查 1 通過是因為 的s1狀態非空,且記錄符合條件Ts - s1.Ts < 5m。 此比對會導致 清除的狀態s1,並將中的s1序列升階為s2。 記錄 8 及其m_id(1) 會被加入狀態和輸出。 -
s1: 檢查1 無關,因為沒有前一步驟,檢查 2 未通過,因為紀錄不符合條件Event == "Start"。
State:
| 步驟 | m_id | s1.Ts | s1.Event | s2.Ts | s2.Event | s3.Ts | s3.Event |
|---|---|---|---|---|---|---|---|
| s1 | X | X | X | X | |||
| s2 | 1 | 00:08:00 | "Start" | 00:11:00 | "E" | X | X |
| s3 | 0 | 00:01:00 | "Start" | 00:03:00 | "D" | 00:04:00 | "Stop" |
紀錄 9
| Ts | Event |
|---|---|
| 12m | "Stop" |
記錄每個步驟的評估:
-
s3: 檢查 1 通過,因為s2非空且符合s3Event == "Stop"條件。 此比對會導致 清除的狀態s2,並將中的s2序列升階為s3。 記錄 9 及其m_id(1) 會被加入狀態和輸出。 -
s2:檢查1未通過是因為的s1狀態為空,檢查2未通過是因為s2缺少活躍序列。 -
s1: 檢查 1 未通過是因為沒有前一步驟,檢查 2 未通過是因為紀錄未符合事件條件 == 「開始」。
State:
| 步驟 | m_id | s1.Ts | s1.Event | s2.Ts | s2.Event | s3.Ts | s3.Event |
|---|---|---|---|---|---|---|---|
| s1 | X | X | X | X | |||
| s2 | X | X | |||||
| s3 | 1 | 00:08:00 | "Start" | 00:11:00 | "E" | 00:12:00 | "Stop" |
最終成果
| Ts | Event | m_id |
|---|---|---|
| 00:01:00 | Start | 0 |
| 00:02:00 | B | 0 |
| 00:03:00 | D | 0 |
| 00:04:00 | Stop | 0 |
| 00:08:00 | Start | 1 |
| 00:11:00 | E | 1 |
| 00:12:00 | Stop | 1 |