Partager via


Jointure de fenêtre de temps

S’applique à : ✅Microsoft FabricAzure Data ExplorerAzure MonitorMicrosoft 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 type string avec ID de corrélation.
  • EventType: colonne de type string qui identifie le type d’événement de l’enregistrement.
  • Timestamp: une colonne de type datetime 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