Window aggregation

Updated at:
Copy as MD

Realtime Compute for Apache Flink supports two types of window aggregation: group window aggregation and window table-valued function (TVF) aggregation. This topic covers their syntax, the scenarios where window TVF aggregation falls back to non-TVF mode, and update stream support across window types.

Choose between the two syntaxes

Group window aggregation Window TVF aggregation
Operator GroupWindowAggregation WindowAggregate
Window functions TUMBLE, HOP, SESSION TUMBLE, HOP, CUMULATE, SESSION
Status Deprecated Recommended
Performance optimizations No Yes
`GROUPING SETS` support No Yes
Window Top-N after aggregation No Yes
Update stream support Yes (VVR) Yes (VVR, all window types)

Use window TVF aggregation. It supports all window types from group window aggregation plus CUMULATE, adds performance optimizations and GROUPING SETS, and lets you apply Window Top-N on aggregation results.

Group window aggregation (deprecated)

Group window aggregation defines windows in the GROUP BY clause. It corresponds to the GroupWindowAggregation operator and supports TUMBLE, HOP, and SESSION window functions.

For syntax, examples, and feature details, see Group window aggregation.

VVR 11.x behavior change for group window aggregation

Starting from VVR 11.x (Flink 1.20), the system no longer automatically rewrites group window aggregation (deprecated syntax) into a window TVF aggregation execution plan.

In VVR 8.x, the system automatically rewrites the deprecated syntax into the new syntax execution plan, enabling Local-Global two-phase aggregation optimization. Starting from VVR 11.x, this automatic rewrite is no longer the default behavior — the deprecated syntax retains its original physical execution plan.

Impact: Jobs using the deprecated syntax no longer benefit from two-phase aggregation optimization automatically. Performance may degrade in scenarios involving large data volumes or data skew.

Important

Migrate group window aggregation to window TVF aggregation (new syntax). For the conditions that activate two-phase aggregation optimization, see Local-Global optimization for window aggregation.

Compatibility with previous behavior

If migration is not immediately possible, enable the following parameter to restore VVR 8.x behavior:

Parameter Description Default
table.optimizer.window-rewrite-enabled Enables the automatic rewrite from deprecated syntax to new syntax, activating two-phase aggregation optimization. false (since VVR 11.x)

Configuration example (add to job parameters):

table.optimizer.window-rewrite-enabled: true
Important

This parameter serves only as a transitional compatibility measure during upgrades. The deprecated syntax may lose rewrite support in future releases. Migrate to the new syntax as soon as possible.

Window TVF aggregation

Window TVF aggregation defines windows through a GROUP BY clause that includes the window_start and window_end columns produced by window TVFs. It corresponds to the WindowAggregate operator and supports TUMBLE, HOP, CUMULATE, and SESSION window functions.

Unlike aggregation on continuous tables, window TVF aggregation produces no intermediate results — only a final result at the end of each window. Intermediate state data is cleaned up automatically.

For syntax, examples, and feature details, see Window TVF aggregation.

SESSION window TVF syntax: VVR 11.x vs VVR 8.x

The SESSION window TVF syntax differs between VVR versions. Upgrade to VVR 11.1 or later to use the full-featured syntax.

VVR 11.x (Flink 1.20)

SESSION(TABLE data [PARTITION BY(keycols, ...)], DESCRIPTOR(timecol), gap)
Parameter Description
data A table with a time attribute column
keycols (Optional) Columns used to partition data before session windowing
timecol The time attribute column mapped to session windows
gap The maximum time interval between two events in the same session

VVR 8.x (Flink 1.17)

SESSION(TABLE data, DESCRIPTOR(timecol), gap)
Parameter Description
data A table with a time attribute column
timecol The time attribute column mapped to session windows
gap The maximum time interval between two events in the same session
VVR 8.x does not support PARTITION BY. Partition fields are inferred implicitly from the GROUP BY clause.

SESSION syntax comparison: VVR 11.x vs VVR 8.x

VVR 11.x VVR 8.x
Syntax SESSION(TABLE data [PARTITION BY(keycols, ...)], DESCRIPTOR(timecol), gap) SESSION(TABLE data, DESCRIPTOR(timecol), gap)
Partition field specification Explicit — via PARTITION BY(keycols) Implicit — via the GROUP BY clause
Partition field restrictions None Must be in GROUP BY; cannot be window_start, window_end, or window_time
Standalone `SESSION()` usage Supported Must be used with GROUP BY
Merging window function with aggregation Supported Not supported — aggregation must match partition fields

The following examples are equivalent. Both use item as the partition field.

-- The Bid table schema (used in all examples below)
> desc Bid;
+-------------+------------------------+------+-----+--------+---------------------------------+
|        name |                   type | null | key | extras |                       watermark |
+-------------+------------------------+------+-----+--------+---------------------------------+
|     bidtime | TIMESTAMP(3) *ROWTIME* | true |     |        | `bidtime` - INTERVAL '1' SECOND |
|       price |         DECIMAL(10, 2) | true |     |        |                                 |
|        item |                 STRING | true |     |        |                                 |
+-------------+------------------------+------+-----+--------+---------------------------------+

-- VVR 11.x: partition field declared explicitly in SESSION()
> SELECT window_start, window_end, item, SUM(price) AS total_price
  FROM TABLE(
      SESSION(TABLE Bid PARTITION BY item, DESCRIPTOR(bidtime), INTERVAL '5' MINUTES))
  GROUP BY item, window_start, window_end;

-- VVR 8.x: partition field inferred from GROUP BY
> SELECT window_start, window_end, item, SUM(price) AS total_price
  FROM TABLE(
      SESSION(TABLE Bid, DESCRIPTOR(bidtime), INTERVAL '5' MINUTES))
  GROUP BY item, window_start, window_end;
VVR 11.x VVR 8.x
SESSION window partitioning SESSION(TABLE Bid PARTITION BY item, DESCRIPTOR(bidtime), INTERVAL '5' MINUTES) SESSION(TABLE Bid, DESCRIPTOR(bidtime), INTERVAL '5' MINUTES)
Aggregation and window merging Direct merging supported (e.g., SUM(price) within the window) Aggregation fields must match window partition fields (e.g., GROUP BY item)

When window TVF aggregation falls back to non-TVF mode

When a query includes a window TVF but does not meet the conditions for the TVF and the aggregation to be merged, the system falls back to a non-TVF execution plan.

Warning

If a non-mergeable query uses processing time as the time attribute, the processing time column is materialized and used as the time attribute of the created windows. This causes the source table's watermark to affect aggregation results — windows may close earlier than expected, and late data may be discarded, the same as with event-time windows. Avoid the patterns below to prevent this.

The window TVF and the aggregation statement cannot be merged when any of the following conditions is met:

  1. Filtering or computation on window time fields. window_start, window_end, or window_time is filtered or modified before aggregation.

    -- Filtering on window_start
    > SELECT window_start, window_end, item, SUM(price) AS total_price
        FROM
        (SELECT item, price, window_start, window_end FROM
        TABLE(
        SESSION(TABLE Bid, DESCRIPTOR(bidtime), INTERVAL '5' MINUTES))
        WHERE window_start >= TIMESTAMP '2020-04-15 08:06:00.000')
        GROUP BY item, window_start, window_end;
    
    -- Arithmetic on window_start
    > SELECT window_start, window_end, item, SUM(price) AS total_price
        FROM
        (SELECT item, price, window_start + (INTERVAL '1' SECOND) AS window_start, window_end FROM
        TABLE(
        SESSION(TABLE Bid, DESCRIPTOR(bidtime), INTERVAL '5' MINUTES)))
        GROUP BY item, window_start, window_end;
    
    -- Type casting on window_start
    > SELECT window_start, window_end, item, SUM(price) AS total_price
        FROM
        (SELECT item, price, CAST(window_start AS varchar) AS window_start, window_end FROM
        TABLE(
        SESSION(TABLE Bid, DESCRIPTOR(bidtime), INTERVAL '5' MINUTES)))
        GROUP BY item, window_start, window_end;
  2. A window TVF is used with a user-defined table-valued function (UDTF).

    > SELECT window_start, window_end, category, SUM(price) AS total_price
        FROM
        (SELECT category, price, window_start, window_end FROM
        TABLE(
        SESSION(TABLE Bid, DESCRIPTOR(bidtime), INTERVAL '5' MINUTES)),
        LATERAL TABLE(category_udtf(item)) AS T(category))
        GROUP BY category, window_start, window_end;
  3. The `GROUP BY` clause is missing `window_start` or `window_end`.

    > SELECT window_start, item, SUM(price) AS total_price
      FROM TABLE(
          SESSION(TABLE Bid, DESCRIPTOR(bidtime), INTERVAL '5' MINUTES))
      GROUP BY item, window_start;
  4. A Python user-defined aggregate function (UDAF) is used.

  5. `GROUPING SETS`, `CUBE`, or `ROLLUP` groups separately by `window_start` or `window_end`.

    > SELECT item, SUM(price) AS total_price
      FROM TABLE(
          SESSION(TABLE Bid, DESCRIPTOR(bidtime), INTERVAL '5' MINUTES))
      GROUP BY GROUPING SETS((item), (window_start), (window_end));
    
    > SELECT item, SUM(price) AS total_price
      FROM TABLE(
          SESSION(TABLE Bid, DESCRIPTOR(bidtime), INTERVAL '5' MINUTES))
      GROUP BY CUBE (item, window_start, window_end);
    
    > SELECT item, SUM(price) AS total_price
      FROM TABLE(
          SESSION(TABLE Bid, DESCRIPTOR(bidtime), INTERVAL '5' MINUTES))
      GROUP BY ROLLUP (item, window_start, window_end);
  6. An aggregate function is applied to `window_start`, `window_end`, or `window_time`.

    > SELECT window_start, window_end, item, SUM(price) AS total_price, MAX(window_end) AS max_end
      FROM TABLE(
          SESSION(TABLE Bid, DESCRIPTOR(bidtime), INTERVAL '5' MINUTES))
      GROUP BY item, window_start, window_end;

Update stream support

The table below shows update stream support by window function and syntax.

Window function Old syntax (GroupWindowAggregation) — VVR Old syntax (GroupWindowAggregation) — Apache Flink New syntax (WindowAggregate) — VVR New syntax (WindowAggregate) — Apache Flink
TUMBLE Yes Yes Yes No
HOP Yes Yes Yes No
SESSION Yes Yes Yes Yes (Apache Flink 1.19 and later)
CUMULATE N/A N/A Yes (VVR 8.0.6 and later) No

In the old syntax, update stream support is identical whether you use VVR or Apache Flink. In the new syntax, only VVR's WindowAggregate operator supports update streams for all window functions. VVR automatically selects between the GroupWindowAggregation and WindowAggregate operators based on the input stream.

For differences between the SESSION window function in VVR and Apache Flink, see Queries.

Local-Global optimization for window aggregation

Window aggregation supports Local-Global two-phase aggregation optimization. When enabled, the optimizer splits a single-phase window aggregation into:

  • Local Aggregate: performs partial pre-aggregation before data shuffle, reducing the amount of data transferred over the network.

  • Global Aggregate: performs the final aggregation after shuffle and produces results.

All six of the following conditions must be met for this optimization to take effect.

Condition 1: Aggregation phase strategy allows two-phase

table.optimizer.agg-phase-strategy is set to AUTO (default) or TWO_PHASE. Setting it to ONE_PHASE disables two-phase optimization.

Condition 2: Window uses event time

The window must use event time (rowtime). Processing-time windows are not supported.

Condition 3: Window type is not SESSION

TUMBLE, HOP, and CUMULATE window types are supported. SESSION windows do not support two-phase optimization.

Condition 4: All aggregate functions support partial merge

All aggregate functions must support the merge operation. Built-in functions such as SUM, COUNT, MIN, MAX, and AVG are supported. Custom UDAFs must implement the merge() method.

Condition 5: Input stream is insert-only and the window can be converted to TVF form

The following conditions must all be met:

  • The input stream is insert-only.

  • table.exec.emit.early-fire.enabled is set to false (default).

  • table.exec.emit.late-fire.enabled is set to false (default).

  • For HOP windows, the window must be aligned (window size is divisible by the slide interval).

Condition 6: Data distribution does not already satisfy partitioning requirements

The input data distribution does not already meet the partitioning requirements for aggregation. If data is already distributed by the partition key, the optimizer determines that no additional pre-aggregation is needed and does not generate a Local Aggregate node.

Parameter reference

Parameter Type Default Requirement
table.optimizer.agg-phase-strategy Enum AUTO Must not be ONE_PHASE
table.exec.emit.early-fire.enabled Boolean false Must be false
table.exec.emit.late-fire.enabled Boolean false Must be false