(+351) 21 24 10006  ·  info@bconcepts.pt
Carnaxide, Lisbon

How to Group Data by Time in KQL: A Step-by-Step Guide

João Barros 05 de July de 2026 4 min read

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 datetime column. In this example we use the Leituras table, with the columns Timestamp, Sensor and Valor.
  • 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 of bin() 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?