시간 창 조인
적용 대상: ✅Microsoft Fabric✅Azure Data Explorer✅Azure Monitor✅Microsoft 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 |
다음 쿼리는 데이터 세트를 만든 다음 이벤트 유형 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 |
시간 창으로 최적화된 예제
이 쿼리를 최적화하기 위해 기간을 고려하여 다시 작성할 수 있습니다. 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 |