4.2 Aggregates: Joins, Group By, and Computed Attributes
Key Takeaways
- Aggregates support two join types: With (INNER JOIN, matching rows only) and Only With (preserves all rows from the first entity, like a left-join on the primary side), chosen when dragging the second entity onto the canvas.
- Group By collapses rows by one or more grouped attributes and computes Count, Sum, Min, Max, or Average per group; every non-aggregated output attribute must appear in the Group By list, mirroring SQL GROUP BY semantics.
- Computed attributes are formula-driven columns added to the Aggregate output (e.g., Order.TotalAmount * 1.20); they execute server-side in the generated SQL when possible and locally on the result set otherwise.
- Aggregates cannot express subqueries, UNION, WITH clauses, arbitrary SQL functions, or writes; these require the SQL element inside a Server Action.
- The boundary between Aggregates and SQL is heavily tested: prefer Aggregates for simple reads, joins, group-by, and computed columns; switch to SQL only when the query exceeds Aggregate capabilities.
Quick Answer: OutSystems Aggregates go beyond single-table reads: you can join multiple entities with With or Only With joins, collapse rows with Group By to compute counts and sums, and add computed attributes — formula-driven columns calculated per row inside the query. When a query exceeds what Aggregates can express (subqueries, UNION, arbitrary SQL functions, or external data), you switch to the SQL element. Knowing the boundary is a heavily tested skill.
Joins: With and Only With
When an Aggregate has more than one source entity, OutSystems generates a join. You add a second entity by dragging it onto the canvas and choosing the relationship. There are two join types available in Aggregates:
| Join type | SQL equivalent | Behavior |
|---|---|---|
| With | INNER JOIN | Returns only rows where the join condition matches in both entities |
| Only With | LEFT-like (preserves first entity) | Returns all rows from the first entity; unmatched second-entity columns are null |
The direction matters. With is symmetric in result; Only With is not — Only With keeps every row from the entity already on the canvas (the first entity), even when there is no match on the second entity you are adding. A common exam formulation: I want every Order even if it has no OrderItem. You would put Order on the canvas first, then add OrderItem with Only With, and the result preserves Orders that have no items. Reversing this gives OrderItem rows without an Order, which is rarely what you want. Joins are defined by a join condition, typically EntityA.Id = EntityB.FKAttribute. The condition is type-checked: identifiers must match. You can add additional filter conditions on top of joins to narrow the result set further.
Group By
Group By collapses rows sharing the same values in one or more grouped attributes and computes an aggregate function over each group. After selecting an attribute to group by, OutSystems exposes the grouping in the Aggregate's output. The supported aggregate functions are:
| Function | Returns |
|---|---|
| Count | Number of rows in each group |
| Sum | Total of a numeric attribute per group |
| Min / Max | Smallest / largest value per group |
| Average | Mean of a numeric attribute per group |
A typical use: list each Customer with the count and total of their Order records. You group by Customer.Id (and any customer display attributes you want in the output), then add Count on Order.Id and Sum on Order.TotalAmount. The output List now has one record per customer with the aggregated columns alongside the customer attributes. A critical constraint: every non-aggregated attribute in the output must be included in the Group By. This mirrors SQL's GROUP BY rule. If you include Customer.Name in the output but forget to group by it, the platform either errors or auto-adds it to the grouping, depending on context — but the exam expects you to know the rule.
Computed Attributes (Calculated Columns)
A computed attribute is a column whose value is calculated per row by a formula, evaluated inside the Aggregate. You add one by right-clicking the output and choosing Add Computed Attribute, then writing an expression such as Order.TotalAmount * 1.20 or If(Order.StatusId = 1, "Pending", "Done"). Computed attributes are part of the generated SQL when possible, so they execute server-side; otherwise the platform evaluates them locally on the result set after it returns. Computed attributes are useful for derived display values (formatted dates, concatenated strings, conditional labels), math on numeric columns (totals, discounts, percentages), and simplifying bindings in the UI by pre-computing values so the client avoids repetitive expressions. They differ from attributes chosen directly from an entity: a computed attribute's value is recalculated for every row at query time, while a direct attribute is just read from the table. This has performance implications — heavy computed attributes on large result sets add CPU cost at query time.
When to Use Aggregates vs SQL
Aggregates cover most read needs, but they cannot express every query. The boundary is a frequent exam topic.
| Use case | Aggregate can | SQL element required |
|---|---|---|
| Simple filter + sort on one entity | Yes | — |
| Inner join of two entities (With) | Yes | — |
| Preserve all first-entity rows (Only With) | Yes | — |
| Group By with Count/Sum | Yes | — |
| Computed column with formula | Yes | — |
| Subquery, UNION, WITH clause | No | Yes |
| UPDATE/INSERT/DELETE statements | No (read-only) | Yes (use SQL or entity actions) |
| Query an external database via Connection | No | Yes |
| Arbitrary SQL functions not in the expression editor | No | Yes |
When you hit one of the SQL-only cases, use the SQL element inside a Server Action, write the query with @-prefixed parameters, and map results to a Structure. The SQL element is covered in detail in section 4.4. For simple reads, always prefer an Aggregate — it is optimized, type-safe, schema-aware, and easier to maintain than hand-written SQL.
You want every Order even if it has no OrderItem rows. Which join should you use?
In an Aggregate with Group By, what must be true of every non-aggregated attribute in the output?
Which of the following requires the SQL element rather than an Aggregate?