Skip to content

Aggregate time windows

tutorial
TL;DR

Create a materialized view to aggregate a streaming table over tumbling or sliding windows. Choose event time or the server’s ingestion timestamp and retain enough hot data for the window. Results arrive after window processing, independently of source acceptance. Reconcile after failures; exactly-once derived output is not guaranteed.

Time windowsTumbling windows separate time; sliding windows overlap
Tumbling windows separate time; sliding windows overlapSix adjacent one-minute tumbling windows partition time. Three five-minute sliding windows start thirty seconds apart; an event at minute two belongs to all three illustrated windows.Tumbling: each interval stands alone0m1m2m3m4m5m6m123456One-minute windows → aggregate after each window closesSliding: the same event can enter multiple windows0–5 min0.5–5.5 min1–6 minEvent at 2mFive-minute width · a new window every 30 secondsIllustrative membership; output follows window processing.Tumbling windows separate time; sliding windows overlapSix adjacent one-minute tumbling windows partition time. Three five-minute sliding windows start thirty seconds apart; an event at minute two belongs to all three illustrated windows.Tumbling: each interval stands alone0m1m2m3m4m5m6m123456One-minute windows; aggregate after closureSliding: overlapping windows0–5 min0.5–5.5 min1–6 minEvent at 2mFive-minute width · starts every 30 secondsOutput follows window processing.
The chart uses the durations in the examples. Window membership differs from publication timing; inspect derived output after processing.

Connect through PGWire to a streaming catalog where you can create tables. Run these examples in a test schema. They use the following source tables:

SQL
CREATE TABLE events (user_id BIGINT, event_type VARCHAR, ts TIMESTAMP);
CREATE TABLE orders (amount DOUBLE, ts TIMESTAMP);
CREATE TABLE trades (price DOUBLE, created_at TIMESTAMP);

Create the views before sending input. Use current timestamps when trying event-time windows, and allow time for a window to close before expecting aggregate results.

Materialized views aggregate data over time windows. They require a WITH clause specifying window parameters.

SQL
-- Tumbling window: 1-minute SUM/COUNT aggregation
CREATE MATERIALIZED VIEW sales_per_minute AS
SELECT SUM(amount) AS total, COUNT(*) AS cnt
FROM orders
WITH (window_type='tumbling', window_size='1 minute', timestamp_column='ts');
SQL
-- Sliding window: 5-minute average sliding every 30 seconds
CREATE MATERIALIZED VIEW avg_price_5m AS
SELECT AVG(price) AS avg_price, COUNT(*) AS cnt
FROM trades
WITH (window_type='sliding', window_size='5 minutes',
slide_interval='30 seconds', timestamp_column='created_at');

If your source data doesn’t have a timestamp column, you can omit timestamp_column to window by ingestion timestamp (__boils_meta_timestamp) instead of event time:

SQL
-- Windows based on when data was ingested by the server
CREATE MATERIALIZED VIEW events_per_minute AS
SELECT COUNT(*) AS cnt
FROM events
WITH (window_type='tumbling', window_size='1 minute');

This is useful for sources without timestamps (Kafka streams, API events) or when ingestion time is the relevant time dimension.

Parameter Required Description
window_type Yes 'tumbling' or 'sliding'
window_size Yes Window duration: '<N> <unit>' where unit is seconds, minutes, hours, or days
timestamp_column No Column used for windowing. If omitted, uses server ingestion timestamp (__boils_meta_timestamp)
slide_interval Sliding only How often the window advances. Must evenly divide window_size
output_topic No Output topic name. Defaults to {view_name}_matview

The output topic contains the columns from your SELECT plus two automatic columns:

  • window_start — start of the aggregation window (TIMESTAMP)
  • window_end — end of the aggregation window (TIMESTAMP)

Materialized views support windowed aggregations, GROUP BY, scalar functions and CASE. An existing WHERE clause filters rows within the selected time window. Validate more complex queries with your server release before depending on them.

SQL
DROP MATERIALIZED VIEW sales_per_minute;
DROP MATERIALIZED VIEW IF EXISTS avg_price_5m;

This stops window processing and deregisters the view. The output topic and its data remain.

Message sequenceWindow output follows closure and processing
Window output follows closure and processingA producer sends source rows. BoilStream assigns them to windows using the configured time basis. Once a window closes, aggregation writes the output topic, which a client queries separately.ProducerBoilStreamSQL clientSend source rowsAssign rows to time windowsClose window and aggregateWrite output topicQuery aggregate resultsVisible window outputTime moves downward · dashed arrows are responsesWindow output follows closure and processingA producer sends source rows. BoilStream assigns them to windows using the configured time basis. Once a window closes, aggregation writes the output topic, which a client queries separately.ProducerBoilStreamSQL clientSend source rowsAssign rows to timewindowsClose window andaggregateWrite output topicQuery aggregate resultsVisible window outputTime moves downward · dashed arrows are responses
Windowed output is not guaranteed exactly once. Reconcile after failures before replaying source input.

A regular view runs when queried. Streaming views transform arriving rows into derived topics; materialized views write results when their windows close. A successful source write does not mean those derived results are already available.

Do not assume materialized-view output triggers every live subscription or derived view. Check the intended workflow before building a chain of dependent outputs. After a failure, reconcile window results before replaying input; exactly-once output is not guaranteed.

Choose the query interface using the compatibility matrix. PGWire and managed DuckDB do not currently promise identical visibility of unpublished recent rows.

window_size must not exceed 80% of hot-tier retention. Keep enough retained data for the windows you intend to calculate.