union
Combines rows from multiple sources — real tables, inline `datatable(...)` literals, or pipeline subqueries — into a single result set.
Combines rows from multiple sources — real tables, inline datatable(...)
literals, or pipeline subqueries — into a single result set. Schemas
combine column-wise (outer-union by default): every column from every
source appears, and rows that lack a column read it as null.
Where it can appear
union works in both leading position (start of a query) and
pipeline position (after |):
union T1, T2, ... // leading
T1 | union T2, T3 // pipeline (LHS counts as the first source)
T1 | summarize ... | union (datatable(...)) // pipeline with non-scan LHSSource kinds
An argument can be a real table name, a parenthesised subquery, or an
inline datatable(...) literal. Mixing kinds within one union is
supported.
withsource provenance
When withsource = Col is set, each row gets a string Col identifying
its source:
- Real table → the table name (e.g.
default). - Inline
datatable(...)→union_arg<N>, where<N>is the source's 0-indexed position in the user's argument list. - Pipeline subquery →
union_arg<N>(treated as a synthetic source).
Restrictions
- Mixed real-table + inline-datatable unions require a
timestamp:datetimecolumn on every inline datatable. Without it, the query's time-range filter would silently drop those inline rows. Berserk rejects this at bind time with a clear error. - Pipeline-position with a real-table RHS (e.g.
T | summarize ... | union T2) parses and binds, but execution is not yet implemented — it needs join-style filter push-down to avoid pulling full table scans to the coordinator. Mix with inline datatables only runs end-to-end today.
Differs from Microsoft KQL
Berserk matches ADX byte-for-byte for all single-level union shapes:
source-name numbering follows user-source order, real tables resolve
withsource to the table name, inline datatables resolve to
union_arg<N>, and schema merging is the column-wise superset.
Chained pipeline withsource differs. When a pipeline-position
union nests inside another (e.g.
union T, (datatable(...)) | union withsource=src (datatable(...))),
Berserk preserves the inner sources' identities (T, union_arg1,
union_arg2). ADX collapses the entire LHS pipeline to a single
opaque union_arg0. Berserk's behaviour is richer and avoids losing
inner provenance, but is not strictly wire-compatible with ADX in
this case. For ADX-portable queries that depend on withsource,
write the union as a single leading-position list rather than
chaining.
Syntax
union T1, T2, ...Combine rows from multiple tables (outer join of schemas)
Parameters
| Name | Description |
|---|---|
| tables | Two or more table names, parenthesised subqueries, or datatable(...) literals |
Syntax
union kind=inner T1, T2, ...Combine rows, keeping only columns common to all tables
Parameters
| Name | Description |
|---|---|
| tables | Two or more table names, parenthesised subqueries, or datatable(...) literals |
Syntax
union withsource=col T1, T2, ...Combine rows with a column identifying the source for each row
Parameters
| Name | Description |
|---|---|
| col | Name for the source column (table name for real tables, union_arg<N> for synthetic sources) |
| tables | Two or more table names, parenthesised subqueries, or datatable(...) literals |
Syntax
T1 | union T2, T3Pipeline-position — LHS is treated as the first source
Parameters
| Name | Description |
|---|---|
| tables | One or more table names, parenthesised subqueries, or datatable(...) literals |
Examples
Example 1 — Combine two warrior rosters
union (datatable(warrior:string, weapon:string)[
"Ragnar", "axe",
"Bjorn", "sword"
]), (datatable(warrior:string, weapon:string)[
"Harald", "spear",
"Rollo", "axe"
])| warrior (string) | weapon (string) |
|---|---|
| Bjorn | sword |
| Harald | spear |
| Ragnar | axe |
| Rollo | axe |
Example 2 — With withsource, inline datatables get union_arg<N> provenance
union withsource = src (datatable(x:long)[
1,
2
]), (datatable(x:long)[
3
])| src (string) | x (long) |
|---|---|
| union_arg0 | 1 |
| union_arg0 | 2 |
| union_arg1 | 3 |