다음을 통해 공유


시간 창 조인

적용 대상: ✅Microsoft FabricAzure Data ExplorerAzure MonitorMicrosoft Sentinel

작업 ID 또는 세션 ID와 같은 일부 높은 카디널리티 키에서 두 개의 큰 데이터 세트 간에 조인하고 왼쪽과 오른쪽에 있는 datetime 열 사이의 "시간 거리"에 대한 제한을 추가하여 각 왼쪽($left) 레코드와 일치해야 하는 오른쪽($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 A 2017-10-01T00:00:00Z
0 B 2017-10-01T00:01:00Z
1 B 2017-10-01T00:02:00Z
1 A 2017-10-01T00:03:00Z
3 A 2017-10-01T00:04:00Z
3 B 2017-10-01T00:10:00Z

다음 쿼리는 데이터 세트를 만든 다음 이벤트 유형 A1min 기간 내에 이벤트 유형이 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

시간 창으로 최적화된 예제

이 쿼리를 최적화하기 위해 기간을 고려하여 다시 작성할 수 있습니다. THe 시간 창은 조인 키로 표현됩니다. 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

5백만 개의 데이터 쿼리

다음 쿼리는 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
  • 조인 연산자