次の方法で共有


時間枠の結合

適用対象: ✅Microsoft FabricAzure Data ExplorerAzure MonitorMicrosoft Sentinel

多くの場合、操作 ID やセッション ID などの高カーディナリティ キーで 2 つの大きなデータセット間を結合し、左側と右側の列の間に "時間距離" の制限を追加することで、左側 ($left) レコードと一致する必要がある右側 (datetime $right) レコードをさらに制限すると便利です。

上記の操作は通常の結合操作とは異なります。左と右のデータセット間でカーディナリティの高いキーを照合する equi-join 部分の場合、システムは距離関数を適用してそれを使用して結合を大幅に高速化することもできます。

手記

距離関数は等値のように動作しません (つまり、dist(x,y) と dist(y,z) の両方が true の場合、その dist(x,z) も true に従いません)。 これは、"対角結合" と呼ばれることもあります。

時間枠のないイベント シーケンスを識別する例

この例では、比較的小さな時間枠内でイベント シーケンスを識別するために、次のスキーマを持つテーブル T を使用します。

  • SessionId: 関連付け ID を持つ string 型の列。
  • EventType: レコードのイベントの種類を識別する string 型の列。
  • Timestamp: datetime 型の列は、レコードによって記述されたイベントがいつ発生するかを示します。
SessionId EventType タイムスタンプ
0 ある 2017-10-01T00:00:00Z
0 B 2017-10-01T00:01:00Z
1 B 2017-10-01T00:02:00Z
1 ある 2017-10-01T00:03:00Z
3 ある 2017-10-01T00:04:00Z
3 B 2017-10-01T00:10:00Z

次のクエリでは、データセットを作成し、イベントの種類 A 後にイベントの種類が 1min 時間枠内で B されたすべてのセッション ID を識別します。

クエリ を実行する

let T = datatable(SessionId:string, EventType:string, Timestamp:datetime)
[
    '0', 'A', datetime(2017-10-01 00:00:00),
    '0', 'B', datetime(2017-10-01 00:01:00),
    '1', 'B', datetime(2017-10-01 00:02:00),
    '1', 'A', datetime(2017-10-01 00:03:00),
    '3', 'A', datetime(2017-10-01 00:04:00),
    '3', 'B', datetime(2017-10-01 00:10:00),
];
T
| where EventType == 'A'
| project SessionId, Start=Timestamp
| join kind=inner
    (
    T 
    | where EventType == 'B'
    | project SessionId, End=Timestamp
    ) on SessionId
| where (End - Start) between (0min .. 1min)
| project SessionId, Start, End 

出力

SessionId 始める 終わり
0 2017-10-01 00:00:00.0000000 2017-10-01 00:01:00.0000000

時間枠で最適化された例

このクエリを最適化するために、時間枠を考慮してクエリを書き直すことができます。 時間枠は結合キーとして表されます。 datetime 値が時間枠の半分のサイズのバケットに "分離" されるようにクエリを書き直します。 equi-join を使用してバケット ID を比較します。

このクエリでは、同じセッション (SessionId) 内のイベントのペアが検索されます。ここで、"A" イベントの後に 1 分以内に "B" イベントが続きます。 セッション ID、'A' イベントの開始時刻、および 'B' イベントの終了時刻を投影します。

クエリ を実行する

let T = datatable(SessionId:string, EventType:string, Timestamp:datetime)
[
    '0', 'A', datetime(2017-10-01 00:00:00),
    '0', 'B', datetime(2017-10-01 00:01:00),
    '1', 'B', datetime(2017-10-01 00:02:00),
    '1', 'A', datetime(2017-10-01 00:03:00),
    '3', 'A', datetime(2017-10-01 00:04:00),
    '3', 'B', datetime(2017-10-01 00:10:00),
];
let lookupWindow = 1min;
let lookupBin = lookupWindow / 2.0;
T 
| where EventType == 'A'
| project SessionId, Start=Timestamp, TimeKey = bin(Timestamp, lookupBin)
| join kind=inner
    (
    T 
    | where EventType == 'B'
    | project SessionId, End=Timestamp,
              TimeKey = range(bin(Timestamp-lookupWindow, lookupBin),
                              bin(Timestamp, lookupBin),
                              lookupBin)
    | mv-expand TimeKey to typeof(datetime)
    ) on SessionId, TimeKey 
| where (End - Start) between (0min .. lookupWindow)
| project SessionId, Start, End 

出力

SessionId 始める 終わり
0 2017-10-01 00:00:00.0000000 2017-10-01 00:01:00.0000000

500 万件のデータ クエリ

次のクエリでは、5M レコードと約 1M セッション ID の広範なデータセットをエミュレートし、時間枠の手法を使用してクエリを実行します。

クエリ を実行する

let T = range x from 1 to 5000000 step 1
| extend SessionId = rand(1000000), EventType = rand(3), Time=datetime(2017-01-01)+(x * 10ms)
| extend EventType = case(EventType < 1, "A",
                          EventType < 2, "B",
                          "C");
let lookupWindow = 1min;
let lookupBin = lookupWindow / 2.0;
T 
| where EventType == 'A'
| project SessionId, Start=Time, TimeKey = bin(Time, lookupBin)
| join kind=inner
    (
    T 
    | where EventType == 'B'
    | project SessionId, End=Time, 
              TimeKey = range(bin(Time-lookupWindow, lookupBin), 
                              bin(Time, lookupBin),
                              lookupBin)
    | mv-expand TimeKey to typeof(datetime)
    ) on SessionId, TimeKey 
| where (End - Start) between (0min .. lookupWindow)
| project SessionId, Start, End 
| count 

出力

数える
3344
  • 結合演算子 する