How to Group Data by Time in KQL: A Step-by-Step Guide
Grouping data into time buckets is the foundation of any real-time analysis: it turns thousands of scattered events into a readable timeline, ready for charts and alerts. In KQL (Kusto Query Language) — the language behind Real-Time Analytics in Microsoft Fabric and Azure Data Explorer — this task comes down to two simple functions: summarize and bin(). This guide shows how to group sensor readings into time windows, from the most basic example to the final chart.
Prerequisites
- Access to a KQL database in Microsoft Fabric (Real-Time Analytics) or an Azure Data Explorer cluster.
- A table with at least one
datetimecolumn. In this example we use theLeiturastable, with the columnsTimestamp,SensorandValor. - Basic query knowledge: knowing that KQL chains steps with the pipe operator (
|).
Step 1: Filter the time window
Before grouping, it is worth limiting the query to the period you care about. This makes the query faster and keeps the chart free of old data. The ago() function returns a point in the past relative to now; here we ask only for the last 24 hours.
Leituras
| where Timestamp > ago(24h)
The result is still the raw rows, one per reading. That is the starting point for grouping.
Step 2: Group with summarize and bin()
The bin() function rounds each timestamp down to the start of the window it belongs to. For example, bin(Timestamp, 15m) turns both 10:07 and 10:14 into 10:00, and 10:22 into 10:15. The summarize operator uses that value to combine all readings in the same window and compute a metric — in this case, the average.
Leituras
| where Timestamp > ago(24h)
| summarize MediaValor = avg(Valor) by bin(Timestamp, 15m)
Now each row represents one 15-minute window with its average. We went from thousands of readings to dozens of points.
Tip: the second argument ofbin()accepts any duration —1m,5m,1h,1d. Start wide (1h) and narrow it only when you need more detail.
Step 3: Group by sensor and by time
Often we want the trend per sensor, not the global average. Just add the Sensor column to the by clause. KQL then creates one window for each combination of sensor and interval.
Leituras
| where Timestamp > ago(24h)
| summarize MediaValor = avg(Valor) by Sensor, bin(Timestamp, 15m)
The order in the by clause does not change the calculation, but it helps you read the table.
Step 4: Compute several metrics and sort
A single query can compute several values per window. Here we combine the average, the maximum and the number of readings, and widen the window to 1 hour. Finally, we sort by time so the timeline is correct.
Leituras
| where Timestamp > ago(24h)
| summarize
MediaValor = avg(Valor),
MaxValor = max(Valor),
NLeituras = count()
by bin(Timestamp, 1h)
| order by Timestamp asc
Note that count() needs no arguments: it counts the rows in each window.
Step 5: Turn it into a chart
To see the trend right away, add the render operator. The timechart type assumes the first column is the time column and draws one line per metric.
Leituras
| where Timestamp > ago(24h)
| summarize MediaValor = avg(Valor) by bin(Timestamp, 1h)
| render timechart
In Real-Time Analytics, this chart can go straight to a dashboard and refresh on its own.
Check the result
To confirm the grouping is correct, check three things: (1) the values in the time column always sit at the start of the window (for example, minutes 00, 15, 30, 45 for 15m windows); (2) there is no Timestamp in the future; and (3) the number of windows makes sense — 24 hours in 1-hour windows give at most 24 rows per sensor. If some intervals are missing, it is because there were no readings in that period — normal with real data.
Conclusion
With summarize and bin() you can already turn raw data into a time series that is ready to analyze. The natural next step is to use make-series to automatically fill the empty windows (useful for continuous charts) and, later, to raise alerts when the average crosses a threshold. What would the ideal time window be for your data: 5 minutes, 1 hour or 1 day?