Jointure de fenêtre de temps
Il est souvent utile de joindre deux jeux de données volumineux sur une clé à cardinalité élevée, comme un ID d’opération ou un ID de session, et de limiter davantage les enregistrements de droite ($right) qui doivent correspondre à chaque enregistrement de gauche ($left) en ajoutant une restriction sur la « distance temporelle » entre datetime
les colonnes à gauche et à droite.
L’opération ci-dessus diffère de l’opération de jointure Kusto habituelle, car pour la equi-join
partie 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.
Notes
Une fonction de distance ne se comporte pas comme une égalité (autrement dit, lorsque dist(x,y) et dist(y,z) sont true, il ne suit pas que dist(x,z) est également true.) En interne, nous l’appelons parfois « jointure diagonale ».
Par exemple, si vous souhaitez identifier des séquences d’événements dans une fenêtre de temps relativement petite, supposons que vous disposez d’une table T
avec le schéma suivant :
SessionId
: colonne de typestring
avec des 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.
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
Sortie
SessionId | Type d’événement | Timestamp |
---|---|---|
0 | A | 2017-10-01 00:00:00.0000000 |
0 | B | 2017-10-01 00:01:00.0000000 |
1 | B | 2017-10-01 00:02:00.0000000 |
1 | A | 2017-10-01 00:03:00.0000000 |
3 | A | 2017-10-01 00:04:00.0000000 |
3 | B | 2017-10-01 00:10:00.0000000 |
Définition du problème
Notre requête doit répondre à la question suivante :
Recherchez tous les ID de session dans lesquels le type A
d’événement a été suivi d’un type B
d’événement dans une 1min
fenêtre de temps.
Notes
Dans les exemples de données ci-dessus, le seul ID de session de ce type est 0
.
Sémantiquement, la requête suivante répond à cette question, bien que de manière inefficace.
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
Sortie
SessionId | Démarrer | End |
---|---|---|
0 | 2017-10-01 00:00:00.0000000 | 2017-10-01 00:01:00.0000000 |
Pour optimiser cette requête, nous pouvons la réécrire comme décrit ci-dessous afin que la fenêtre de temps soit exprimée sous forme de clé de jointure.
Réécrire la requête pour tenir compte de la fenêtre de temps
Réécrire la requête afin que les datetime
valeurs soient « discrétisées » dans des compartiments dont la taille est la moitié de la taille de la fenêtre de temps. Utilisez Kusto equi-join
pour comparer ces ID de compartiment.
let lookupWindow = 1min;
let lookupBin = lookupWindow / 2.0; // lookup bin = equal to 1/2 of the lookup window
T
| where EventType == 'A'
| project SessionId, Start=Timestamp,
// TimeKey on the left side of the join is mapped to a discrete time axis for the join purpose
TimeKey = bin(Timestamp, lookupBin)
| join kind=inner
(
T
| where EventType == 'B'
| project SessionId, End=Timestamp,
// TimeKey on the right side of the join - emulates event 'B' appearing several times
// as if it was 'replicated'
TimeKey = range(bin(Timestamp-lookupWindow, lookupBin),
bin(Timestamp, lookupBin),
lookupBin)
// 'mv-expand' translates the TimeKey array range into a column
| mv-expand TimeKey to typeof(datetime)
) on SessionId, TimeKey
| where (End - Start) between (0min .. lookupWindow)
| project SessionId, Start, End
Référence de requête exécutable (avec table insérée)
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
Sortie
SessionId | Démarrer | End |
---|---|---|
0 | 2017-10-01 00:00:00.0000000 | 2017-10-01 00:01:00.0000000 |
Requête de données de 5 millions
La requête suivante émule un jeu de données de 5 millions d’enregistrements et environ 1 millions d’ID et exécute la requête avec la technique décrite ci-dessus.
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
Sortie
Count |
---|
3344 |
Commentaires
https://aka.ms/ContentUserFeedback.
Bientôt disponible : Tout au long de 2024, nous allons supprimer progressivement GitHub Issues comme mécanisme de commentaires pour le contenu et le remplacer par un nouveau système de commentaires. Pour plus d’informations, consultezEnvoyer et afficher des commentaires pour