掃描運算子
掃描資料、比對和組建以述詞為基礎的序列。
系統會根據運算子步驟中所定義的述詞來判定比對記錄。 述詞可取決於先前步驟所產生的狀態。 比對記錄的輸出是由運算子步驟中所定義的輸入記錄和指派所決定。
語法
T| scan
[ with_match_id
=
MatchIdColumnName ] [ declare
(
ColumnDeclarations)
] with
(
StepDefinitions)
ColumnDeclarations 語法
ColumnName:
ColumnType[=
DefaultValue ] [,
... ]
StepDefinition 語法
step
StepName [ output
= all
| last
| none
] :
Condition [ =>
Column=
Assignment [,
... ] ] ;
深入瞭解 語法慣例。
參數
名稱 | 類型 | 必要 | Description |
---|---|---|---|
T | string |
✔️ | 輸入表格式來源。 |
MatchIdColumnName | string |
在掃描執行過程中附加至輸出之型 long 別的數據行名稱。 指出記錄相符專案的 0 型索引。 |
|
ColumnDeclarations | string |
宣告 T 架構的延伸模組。這些步驟中會指派這些數據行的值。 如果未指派,則會傳回 DefaultValue 。 除非另有指定, 否則 DefaultValue 為 null 。 |
|
StepName | string |
✔️ | 用來參考掃描條件和指派狀態中的值。 步驟名稱必須是唯一的。 |
條件 | string |
✔️ | 評估為 true 或 false 的表示式,定義輸入中的哪些記錄符合步驟。 當條件為 true 步驟狀態或上一個步驟的狀態時,記錄會比對步驟。 |
指派 | string |
當記錄符合步驟時,指派給對應數據行的純量表達式。 | |
output |
string |
控制重複相符專案上步驟的輸出邏輯。 all 會輸出所有符合步驟的記錄、 last 只輸出步驟一系列重複比對中的最後一筆記錄,而且 none 不會輸出符合步驟的記錄。 預設為 all 。 |
傳回
從輸入到步驟的各記錄相符項目的記錄。 輸出的結構描述是在 declare
子句中使用資料行擴充的來源結構描述。
掃描邏輯
scan
會逐記錄仔細檢查已序列化的輸入資料,將每一筆記錄與每個步驟的條件進行比較,同時考慮每個步驟的目前狀態。
狀態
運算子的基礎狀態 scan
可以視為具有每個 step
數據列的數據表。 每個步驟都會使用所有先前步驟和目前步驟中的最新數據行值和宣告的變數來維護自己的狀態。 如果相關,它也會保存進行中序列的比對標識符。
如果掃描運算符具有名為 s_1、s_2...、... s_n的 n 個步驟,則步驟s_k的狀態會對應至s_1、s_2...、s_k。 StepName。ColumnName 格式是用來參考狀態中的值。 例如, s_2.col1
會參考 col1
屬於s_k狀態之步驟 s_2 的數據 行。 如需詳細的範例,請參閱 掃描邏輯逐步解說。
每當掃描的輸入記錄符合步驟時,狀態就會啟動空白並更新。 當目前步驟的狀態為無空時,步驟稱為具有 作用中序列。
比對邏輯
每個輸入記錄都會以反向順序評估所有步驟,從最後一個步驟到第一個步驟。 當記錄 r 針對某些步驟 s_k進行評估時,會套用下列邏輯:
檢查 1:如果上一個步驟的狀態 (s_k-1) 為 nonempty,且 r 符合s_k的條件,則會發生比對。 比對會導致下列動作:
- 已清除 s_k 的狀態。
- s_k-1 的狀態會升階為s_k的狀態。
- 計算 s_k 指派並擴充 r。
- 擴充 r 會新增至輸出和 s_k的狀態。
注意
如果 Check 1 會產生相符專案,則會忽略 Check 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 是第一個步驟,則會開始新的序列,且比對標識符會依
1
增加。 這只會影響使用with_match_id
時的輸出。
一旦檢查 s_k 完成, r 會繼續針對 s_k-1 進行評估。
如需此邏輯的詳細範例,請參閱 掃描邏輯逐步解說。
範例
累計總和
計算輸入資料行的累計總和。 此範例的結果相當於使用 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;
)
輸出
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);
)
輸出
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);
)
輸出
Ts | 事件 | 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 分鐘之後結束,之後就會開始新的工作階段。 請注意,旗標的使用 with_match_id
會為每個相異比對指派唯一值, (會話) 掃描。 另請注意此範例中這兩個步驟的特殊用途,inSession
以 true
作為條件,因此其會從輸入中擷取並輸出所有記錄,而 endSession
會從目前相符項目的 sessionStart
值中擷取超過 30 分鐘的記錄。 endSession
步驟具有 output=none
,代表其不會產生輸出記錄。 endSession
步驟是用來將目前相符項目的狀態從 inSession
向前移至 endSession
,讓新的相符項目 (工作階段) 開始 (從目前的記錄開始)。
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;
)
輸出
Ts | 事件 | 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 |
開始與停止之間的事件
尋找在 5 分鐘內事件 Start
和事件 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;
)
輸出
Ts | 事件 | m_id |
---|---|---|
00:01:00 | 開始 | 0 |
00:02:00 | B | 0 |
00:03:00 | D | 0 |
00:04:00 | Stop | 0 |
00:08:00 | 開始 | 1 |
00:11:00 | E | 1 |
00:12:00 | Stop | 1 |
計算事件的自訂漏斗圖
按 State
計算序列 Hail
->Tornado
->Thunderstorm Wind
的漏斗圖完成情況,並在事件之間的時間上使用自訂閾值 (1h
內的 Tornado
和 2h
內的 Thunderstorm Wind
)。 此範例類似於 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
輸出
EventType | dcount_State |
---|---|
Hail | 50 |
龍捲風 | 34 |
Thunderstorm Wind | 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;
)
狀態
將運算符的狀態 scan
視為每個步驟有一個數據列的數據表,其中每個步驟都有自己的狀態。 此狀態包含所有先前步驟和目前步驟中數據行和宣告變數的最新值。 若要深入瞭解,請參閱 狀態。
在此範例中,狀態可以使用下表來表示:
步驟 | m_id | s1.Ts | s1.事件 | s2.Ts | s2.事件 | s3.Ts | s3.事件 |
---|---|---|---|---|---|---|---|
s1 | X | X | X | X | |||
s2 | X | X | |||||
S3 |
“X” 表示特定欄位與該步驟無關。
比對邏輯
本節會透過數據表的每個記錄Events
遵循比對邏輯,說明每個步驟的狀態和輸出轉換。
注意
輸入記錄會根據反向順序的步驟進行評估,從最後一個步驟 () s3
到第一個步驟 (s1
) 。
記錄 1
Ts | 事件 |
---|---|
0m | "A" |
在每個步驟記錄評估:
s3
: 檢查 1 不會傳遞,因為 狀態s2
是空的, 而且檢查 2 不會通過,因為s3
缺少作用中序列。s2
: 檢查 1 不會傳遞,因為 狀態s1
是空的, 而且檢查 2 不會通過,因為s2
缺少作用中序列。s1
: 檢查 1 無關,因為沒有上一個步驟。 檢查 2 未通過,因為記錄不符合 的條件Event == "Start"
。 記錄 1 會捨棄,而不會影響狀態或輸出。
狀態:
步驟 | m_id | s1.Ts | s1.事件 | s2.Ts | s2.事件 | s3.Ts | s3.事件 |
---|---|---|---|---|---|---|---|
s1 | X | X | X | X | |||
s2 | X | X | |||||
S3 |
記錄 2
Ts | 事件 |
---|---|
1m | "Start" |
在每個步驟記錄評估:
s3
: 檢查 1 不會傳遞,因為 狀態s2
是空的, 而且檢查 2 不會通過,因為s3
缺少作用中序列。s2
: 檢查 1 不會傳遞,因為 狀態s1
是空的, 而且檢查 2 不會通過,因為s2
缺少作用中序列。s1
: 檢查 1 無關,因為沒有上一個步驟。 檢查 2 已通過,因為記錄符合 的條件Event == "Start"
。 此比對會起始新的序列,並m_id
指派 。 記錄 2 及其m_id
(0
) 會新增至狀態和輸出。
狀態:
步驟 | m_id | s1.Ts | s1.事件 | s2.Ts | s2.事件 | s3.Ts | s3.事件 |
---|---|---|---|---|---|---|---|
s1 | 0 | 00:01:00 | "Start" | X | X | X | X |
s2 | X | X | |||||
S3 |
記錄 3
Ts | 事件 |
---|---|
2m | "B" |
在每個步驟記錄評估:
s3
: 檢查 1 不會傳遞,因為 狀態s2
是空的, 而且檢查 2 不會通過,因為s3
缺少作用中序列。s2
: 檢查 1 會傳遞,因為 的狀態s1
為無空,且記錄符合 的條件Ts - s1.Ts < 5m
。 此比對會導致 清除的狀態s1
,並將中的s1
序列升階為s2
。 記錄 3 及其m_id
(0
) 會新增至狀態和輸出。s1
: 檢查 1 無關,因為沒有上一個步驟,而且 不會傳遞 Check 2 ,因為記錄不符合 的條件Event == "Start"
。
狀態:
步驟 | m_id | s1.Ts | s1.事件 | s2.Ts | s2.事件 | s3.Ts | s3.事件 |
---|---|---|---|---|---|---|---|
s1 | X | X | X | X | |||
s2 | 0 | 00:01:00 | "Start" | 00:02:00 | "B" | X | X |
S3 |
記錄 4
Ts | 事件 |
---|---|
3m | "D" |
在每個步驟記錄評估:
s3
: 檢查 1 未通過,因為記錄不符合Event == "Stop"
的條件,而且 檢查 2 不會通過,因為s3
缺少使用中序列。s2
: 檢查 1 不會傳遞,因為 的狀態是空的s1
。 它會通過 Check 2 ,因為它符合 的條件Ts - s1.Ts < 5m
。 記錄 4 及其m_id
(0
) 會新增至狀態和輸出。 此記錄中的值會覆寫 和s2.Event
的先前狀態值s2.Ts
。s1
: 檢查 1 無關,因為沒有上一個步驟,而且 不會傳遞 Check 2 ,因為記錄不符合 的條件Event == "Start"
。
狀態:
步驟 | m_id | s1.Ts | s1.事件 | s2.Ts | s2.事件 | s3.Ts | s3.事件 |
---|---|---|---|---|---|---|---|
s1 | X | X | X | X | |||
s2 | 0 | 00:01:00 | "Start" | 00:03:00 | "D" | X | X |
S3 |
記錄 5
Ts | 事件 |
---|---|
4m | “Stop” |
在每個步驟記錄評估:
s3
: 檢查 1 已傳遞,因為s2
是無空,且符合s3
的條件Event == "Stop"
。 此比對會導致 清除的狀態s2
,並將中的s2
序列升階為s3
。 記錄 5 及其m_id
(0
) 會新增至狀態和輸出。s2
: 檢查 1 不會傳遞,因為 狀態s1
是空的, 而且檢查 2 不會通過,因為s2
缺少作用中序列。s1
: 檢查 1 無關,因為沒有上一個步驟。 檢查 2 未通過,因為記錄不符合 的條件Event == "Start"
。
狀態:
步驟 | m_id | s1.Ts | s1.事件 | s2.Ts | s2.事件 | s3.Ts | s3.事件 |
---|---|---|---|---|---|---|---|
s1 | X | X | X | X | |||
s2 | X | X | |||||
S3 | 0 | 00:01:00 | "Start" | 00:03:00 | "D" | 00:04:00 | “Stop” |
記錄 6
Ts | 事件 |
---|---|
6m | "C" |
在每個步驟記錄評估:
s3
: 檢查 1 不會通過,因為 的狀態s2
是空的, 而且檢查 2 不會通過,因為s3
不符合s3
的條件Event == "Stop"
。s2
: 檢查 1 不會傳遞,因為 狀態s1
是空的, 而且檢查 2 不會通過,因為s2
缺少作用中序列。s1
: 檢查 1 未通過,因為沒有上一個步驟,而且 不會通過 Check 2 ,因為它不符合 的條件Event == "Start"
。 記錄 6 會捨棄,而不會影響狀態或輸出。
狀態:
步驟 | m_id | s1.Ts | s1.事件 | s2.Ts | s2.事件 | s3.Ts | s3.事件 |
---|---|---|---|---|---|---|---|
s1 | X | X | X | X | |||
s2 | X | X | |||||
S3 | 0 | 00:01:00 | "Start" | 00:03:00 | "D" | 00:04:00 | “Stop” |
記錄 7
Ts | 事件 |
---|---|
8m | "Start" |
在每個步驟記錄評估:
s3
: 檢查 1 未通過,因為 的狀態s2
是空的, 而且檢查 2 未通過,因為它不符合 的條件Event == "Stop"
。s2
: 檢查 1 不會傳遞,因為 狀態s1
是空的, 而且檢查 2 不會通過,因為s2
缺少作用中序列。s1
: 檢查 1 未通過,因為沒有上一個步驟。 它會通過 Check 2 ,因為它符合 的條件Event == "Start"
。 此比對會使用新的 起始新的m_id
序列s1
。 記錄 7 及其m_id
(1
) 會新增至狀態和輸出。
狀態:
步驟 | m_id | s1.Ts | s1.事件 | s2.Ts | s2.事件 | s3.Ts | s3.事件 |
---|---|---|---|---|---|---|---|
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” |
注意
狀態現在有兩個作用中的序列。
記錄8
Ts | 事件 |
---|---|
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 不相關,因為沒有上一個步驟, 而且不會通過 Check 2 ,因為記錄不符合 的條件Event == "Start"
。
狀態:
步驟 | m_id | s1.Ts | s1.事件 | s2.Ts | s2.事件 | s3.Ts | s3.事件 |
---|---|---|---|---|---|---|---|
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 | 事件 |
---|---|
12m | “Stop” |
記錄每個步驟的評估:
s3
: 檢查 1 已傳遞,因為s2
為無空,且符合s3
的條件Event == "Stop"
。 此比對會導致 清除的狀態s2
,並將中的s2
序列升階為s3
。 記錄 9 及其m_id
()1
會新增至狀態和輸出。s2
: 檢查 1 不會傳遞,因為 的狀態s1
是空的, 而且檢查 2 不會傳遞,因為s2
缺少使用中序列。s1
: 檢查 1 未通過,因為沒有上一個步驟。 它會通過 Check 2 ,因為它符合 的條件Event == "Start"
。 此比對會以新的m_id
起始新的s1
序列。
狀態:
步驟 | m_id | s1.Ts | s1.事件 | s2.Ts | s2.事件 | s3.Ts | s3.事件 |
---|---|---|---|---|---|---|---|
s1 | X | X | X | X | |||
s2 | X | X | |||||
S3 | 1 | 00:08:00 | "Start" | 00:11:00 | “E” | 00:12:00 | “Stop” |
最終輸出
Ts | 事件 | m_id |
---|---|---|
00:01:00 | 開始 | 0 |
00:02:00 | B | 0 |
00:03:00 | D | 0 |
00:04:00 | Stop | 0 |
00:08:00 | 開始 | 1 |
00:11:00 | E | 1 |
00:12:00 | Stop | 1 |
意見反應
https://aka.ms/ContentUserFeedback。
即將登場:在 2024 年,我們將逐步淘汰 GitHub 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應