Skip to main content
It is a common operation for tables that contain an array column to produce a new table that has a row with each individual array element of that initial column, while values of other columns are duplicated. This is the basic case of what ARRAY JOIN clause does. Its name comes from the fact that it can be looked at as executing JOIN with an array or nested data structure. The intent is similar to the arrayJoin function, but the clause functionality is broader.
PostgreSQL’s FROM unnest(...), CROSS JOIN UNNEST(...) and LATERAL unnest(...) table-source forms are not supported. Use ARRAY JOIN instead. The unnest name (since version 26.5) is a function-call alias of arrayJoin (SELECT unnest(arr)), not a table function. JOIN LATERAL <subquery> over a correlated subquery is supported separately as an experimental feature, see JOIN.
Syntax:
Supported types of ARRAY JOIN are listed below:
  • ARRAY JOIN - In base case, empty arrays are not included in the result of JOIN.
  • LEFT ARRAY JOIN - The result of JOIN contains rows with empty arrays. The value for an empty array is set to the default value for the array element type (usually 0, empty string or NULL).

Basic ARRAY JOIN Examples

ARRAY JOIN and LEFT ARRAY JOIN

The examples below demonstrate the usage of the ARRAY JOIN and LEFT ARRAY JOIN clauses. Let’s create a table with an Array type column and insert values into it:
The example below uses the ARRAY JOIN clause:
The next example uses the LEFT ARRAY JOIN clause:

ARRAY JOIN and arrayEnumerate function

This function is normally used with ARRAY JOIN. It allows counting something just once for each array after applying ARRAY JOIN. Example:
In this example, Reaches is the number of conversions (the strings received after applying ARRAY JOIN), and Hits is the number of pageviews (strings before ARRAY JOIN). In this particular case, you can get the same result in an easier way:

ARRAY JOIN and arrayEnumerateUniq

This function is useful when using ARRAY JOIN and aggregating array elements. In this example, each goal ID has a calculation of the number of conversions (each element in the Goals nested data structure is a goal that was reached, which we refer to as a conversion) and the number of sessions. Without ARRAY JOIN, we would have counted the number of sessions as sum(Sign). But in this particular case, the rows were multiplied by the nested Goals structure, so in order to count each session one time after this, we apply a condition to the value of the arrayEnumerateUniq(Goals.ID) function.

Using Aliases

An alias can be specified for an array in the ARRAY JOIN clause. In this case, an array item can be accessed by this alias, but the array itself is accessed by the original name. Example:
Using aliases, you can perform ARRAY JOIN with an external array. For example:
Multiple arrays can be comma-separated in the ARRAY JOIN clause. In this case, JOIN is performed with them simultaneously (the direct sum, not the cartesian product). Note that all the arrays must have the same size by default. Example:
The example below uses the arrayEnumerate function:
Multiple arrays with different sizes can be joined by using: SETTINGS enable_unaligned_array_join = 1. Example:

ARRAY JOIN with Nested Data Structure

ARRAY JOIN also works with nested data structures:
When specifying names of nested data structures in ARRAY JOIN, the meaning is the same as ARRAY JOIN with all the array elements that it consists of. Examples are listed below:
This variation also makes sense:
An alias may be used for a nested data structure, in order to select either the JOIN result or the source array. Example:
Example of using the arrayEnumerate function:

Row Order and Paging

ARRAY JOIN keeps the order of the rows that it receives. For each row, it produces the elements in their order in the array. If a query has more than one ARRAY JOIN clause, the first clause is the outer loop:
If one ARRAY JOIN clause has more than one array, for example ARRAY JOIN xs AS x, ys AS y, the elements at the same position go to the same row. In this example, the result is the two rows ('a', 1) and ('b', 2). Without ORDER BY, the order of the rows that ARRAY JOIN receives is not guaranteed. For example, a query can read a table in parallel streams. To get a stable order, add ORDER BY with the columns that identify the source row and the position of the element. You must do this to read the result in pages with LIMIT and OFFSET:
If ORDER BY starts with the columns of the sorting key of a MergeTree table, the query reads the table in the order of the key. In the example, this is true when the table has ORDER BY id. The query then stops when it has the rows for LIMIT, and it does not expand the arrays of the other rows. For more information, see Optimization of Data Reading and the setting optimize_read_in_order. If ORDER BY starts with an element of the array, for example ORDER BY num, id, the query expands and sorts all the rows. The arrayJoin function also produces the elements of each array in their order in the array. But if a query has more than one arrayJoin call, the first call is not always the outer loop. For more information, see Row order.

Implementation Details

The query execution order is optimized when running ARRAY JOIN. Although ARRAY JOIN must always be specified before the WHERE/PREWHERE clause in a query, technically they can be performed in any order, unless result of ARRAY JOIN is used for filtering. The processing order is controlled by the query optimizer.

Incompatibility with short-circuit function evaluation

Short-circuit function evaluation is a feature that optimizes the execution of complex expressions in specific functions such as if, multiIf, and, and or. It prevents potential exceptions, such as division by zero, from occurring during the execution of these functions. arrayJoin is always executed and not supported for short circuit function evaluation. That’s because it’s a unique function processed separately from all other functions during query analysis and execution and requires additional logic that doesn’t work with short circuit function execution. The reason is that the number of rows in the result depends on the arrayJoin result, and it’s too complex and expensive to implement lazy execution of arrayJoin.