Jointure de fenêtre de temps
S’applique à : ✅Microsoft Fabric✅Azure Data Explorer✅Azure Monitor✅Microsoft Sentinel
Il est souvent utile de joindre entre deux jeux de données volumineux sur une clé de cardinalité élevée, comme un ID d’opération ou un ID de session, et limiter davantage les enregistrements de droite ($right) qui doivent correspondre à chaque enregistrement de gauche ($left) en ajoutant une restriction sur la « distance temporelle » entre les colonnes datetime
à gauche et à droite.
L’opération ci-dessus diffère de l’opération de jointure habituelle, car pour la partie equi-join
de la correspondance de la clé de cardinalité élevée entre les jeux de données de gauche et de droite, le système peut également appliquer une fonction de distance et l’utiliser pour accélérer considérablement la jointure.
Note
Une fonction de distance ne se comporte pas comme l’égalité (autrement dit, lorsque les deux dist(x,y) et dist(y,z) sont vrais, il ne suit pas que dist(x,z) est également vrai.) Il s’agit parfois d’une « jointure diagonale ».
Exemple d’identification des séquences d’événements sans fenêtre de temps
Pour identifier les séquences d’événements dans une fenêtre de temps relativement petite, cet exemple utilise une table T
avec le schéma suivant :
-
SessionId
: colonne de typestring
avec ID de corrélation. -
EventType
: colonne de typestring
qui identifie le type d’événement de l’enregistrement. -
Timestamp
: une colonne de typedatetime
indique quand l’événement décrit par l’enregistrement s’est produit.
SessionId | EventType | Horodatage |
---|---|---|
0 | Un | 2017-10-01T00:00:00Z |
0 | B | 2017-10-01T00:01:00Z |
1 | B | 2017-10-01T00:02:00Z |
1 | Un | 2017-10-01T00:03:00Z |
3 | Un | 2017-10-01T00:04:00Z |
3 | B | 2017-10-01T00:10:00Z |
La requête suivante crée le jeu de données, puis identifie tous les ID de session dans lesquels le type d’événement A
a été suivi d’un type d’événement B
dans une fenêtre de temps 1min
.
Exécuter le de requête
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
de sortie
SessionId | Commencer | Fin |
---|---|---|
0 | 2017-10-01 00:00:00.0000000 | 2017-10-01 00:01:00.0000000 |
Exemple optimisé avec la fenêtre de temps
Pour optimiser cette requête, nous pouvons la réécrire pour prendre en compte la fenêtre de temps. La fenêtre de temps THe est exprimée sous la forme d’une clé de jointure. Réécrire la requête afin que les valeurs datetime
soient « discrétisées » dans des compartiments dont la taille correspond à la moitié de la taille de la fenêtre de temps. Utilisez equi-join
pour comparer les ID de compartiment.
La requête recherche des paires d’événements dans la même session (SessionId) où un événement « A » est suivi d’un événement « B » dans un délai de 1 minute. Il projette l’ID de session, l’heure de début de l’événement « A » et l’heure de fin de l’événement « B ».
Exécuter le de requête
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
de sortie
SessionId | Commencer | Fin |
---|---|---|
0 | 2017-10-01 00:00:00.0000000 | 2017-10-01 00:01:00.0000000 |
5 millions de requêtes de données
La requête suivante émule un jeu de données complet de 5 Millions d’enregistrements et environ 1M ID de session et exécute la requête avec la technique de fenêtre de temps.
Exécuter le de requête
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
de sortie
Compter |
---|
3344 |