UNNEST

更新时间:
复制 MD 格式

The UNNEST operator expands an ARRAY expression into one output row per element. It works with both plain arrays and ARRAY<STRUCT> types. UNNEST can run standalone or combined with JOIN to expand an array column from a table.

Expand an array with UNNEST

UNNEST turns an array expression into a row set — one row per element. Use it in the FROM clause, just like a table reference.

Syntax

SELECT ... FROM UNNEST (<expression>) [AS <alias>];

Parameters

  • expression: Required. An ARRAY expression to expand.

  • alias: Optional. A name for the expanded column. Without an alias, the system auto-generates a column name such as __generated_unnest_col_c0.

Examples

The following examples show how the auto-generated column name compares to a user-defined alias.

SELECT * FROM unnest(array(1,2,3));

-- Result: system generates the column name
+---------------------------+
| __generated_unnest_col_c0 |
+---------------------------+
| 1                         |
| 2                         |
| 3                         |
+---------------------------+

SELECT * FROM unnest(array(1,2,3)) t;

-- Result: alias "t" is used as the column name
+------+
| t    |
+------+
| 1    |
| 2    |
| 3    |
+------+

Expand a STRUCT array with UNNEST

When the input is an ARRAY<STRUCT>, UNNEST expands the top-level fields of each STRUCT into separate columns. Nested STRUCTs are not expanded further — only the outermost STRUCT fields become columns.

Syntax

SELECT ... FROM UNNEST (<expression>) [AS <alias>];

Parameters

  • expression: Required. An ARRAY<STRUCT> expression to expand.

  • alias: Optional. A name for the expanded STRUCT. Reference individual fields using <alias>.field notation, or use field names directly without the alias prefix.

Examples

Example 1: Basic STRUCT expansion — fields become columns

SELECT * FROM UNNEST(ARRAY(STRUCT(1 AS a, 2 AS b), STRUCT(3 AS a, 4 AS b)));

-- Result: 2 rows, each STRUCT field becomes a column
+------+------+
| a    | b    |
+------+------+
| 1    | 2    |
| 3    | 4    |
+------+------+

SELECT *, t FROM UNNEST(ARRAY(STRUCT(1 AS a, 2 AS b), STRUCT(3 AS a, 4 AS b))) t;

-- Result: alias "t" adds a column containing the full STRUCT value
+------+------+------------+
| a    | b    | t          |
+------+------+------------+
| 1    | 2    | {a:1, b:2} |
| 3    | 4    | {a:3, b:4} |
+------+------+------------+

Example 2: Access STRUCT fields by name or via the <alias>.field notation

SELECT a, t.a FROM UNNEST(ARRAY(STRUCT(1 AS a, 2 AS b), STRUCT(3 AS a, 4 AS b))) t;

-- Result: "a" and "t.a" resolve to the same field; the duplicate is renamed "a_1"
+------+------+
| a    | a_1  |
+------+------+
| 1    | 1    |
| 3    | 3    |
+------+------+

Example 3: Nested STRUCTs — only the top-level fields are expanded

UNNEST expands only the outermost fields. A nested STRUCT appears as a single column containing the full STRUCT value, not as individual columns.

SELECT * FROM UNNEST(ARRAY(STRUCT(STRUCT(1 AS a1, 2 AS a2) AS a, 3 AS b), STRUCT(STRUCT(4 AS a1, 5 AS a2) AS a, 6 AS b))) t;

-- Result: "a" is a nested STRUCT and stays as a single column; only "b" is a plain value
+--------------+------+
| a            | b    |
+--------------+------+
| {a1:1, a2:2} | 3    |
| {a1:4, a2:5} | 6    |
+--------------+------+

Use UNNEST with JOIN

UNNEST and JOIN are both FROM clause constructs and can be combined. Place UNNEST on the right side of a JOIN and pass an ARRAY column from the left table — UNNEST expands that column laterally, producing one output row per array element.

Syntax

SELECT ... FROM ... JOIN UNNEST (<expression>) [AS <alias>] [ON <join_condition>]

Parameters

  • expression: Required. An ARRAY column from the left-side table in the JOIN.

  • alias: Optional. A name for the expanded element.

  • join_condition: Optional. One or more equality expressions that filter the join results. Format: ON equality_expression [AND equality_expression]...

Examples

Example 1: Expand a plain integer array from a table column

The referenced column must be an ARRAY type. Without an alias, the expanded column gets an auto-generated name; with an alias, use it to refer to each element.

WITH t AS (SELECT * FROM VALUES (ARRAY(1,2,3)) t(a))
SELECT * FROM t JOIN UNNEST(t.a);

-- Result: 3 rows — one per element; column name is auto-generated
+-----------+---------------------------+
| a         | __generated_unnest_col_c0 |
+-----------+---------------------------+
| [1, 2, 3] | 1                         |
| [1, 2, 3] | 2                         |
| [1, 2, 3] | 3                         |
+-----------+---------------------------+

WITH t AS (SELECT * FROM VALUES (ARRAY(1,2,3)) t(a))
SELECT * FROM t JOIN UNNEST(t.a) col;

-- Result: 3 rows — alias "col" names the expanded column
+-----------+------+
| a         | col  |
+-----------+------+
| [1, 2, 3] | 1    |
| [1, 2, 3] | 2    |
| [1, 2, 3] | 3    |
+-----------+------+

Example 2: Expand an ARRAY<STRUCT> column — STRUCT fields become columns

When the array elements are STRUCTs, UNNEST expands each STRUCT field into a separate column. Use an alias to reference specific fields with <alias>.field.

WITH t AS (SELECT * FROM VALUES (ARRAY(STRUCT(1 AS f1, 2 AS f2), STRUCT(3 AS f1, 4 AS f2))) t(a))
SELECT * FROM t JOIN UNNEST(t.a);

-- Result: 2 rows; f1 and f2 expand into separate columns
+------------------------------+------+------+
| a                            | f1   | f2   |
+------------------------------+------+------+
| [{f1:1, f2:2}, {f1:3, f2:4}] | 1    | 2    |
| [{f1:1, f2:2}, {f1:3, f2:4}] | 3    | 4    |
+------------------------------+------+------+

WITH t AS (SELECT * FROM VALUES (ARRAY(STRUCT(1 AS f1, 2 AS f2), STRUCT(3 AS f1, 4 AS f2))) t(a))
SELECT *, col.f1 FROM t JOIN UNNEST(t.a) col;

-- Result: alias "col" lets you reference individual fields like col.f1
+------------------------------+------+------+------+
| a                            | f1   | f2   | f1_1 |
+------------------------------+------+------+------+
| [{f1:1, f2:2}, {f1:3, f2:4}] | 1    | 2    | 1    |
| [{f1:1, f2:2}, {f1:3, f2:4}] | 3    | 4    | 3    |
+------------------------------+------+------+------+

Example 3: Filter with an ON condition

Use ON to keep only the rows where an expanded element matches a value in the left table. If the array column can be NULL or empty, use LEFT JOIN to avoid silently dropping the entire row.

WITH user_configs AS (
  SELECT 
    'user_001' AS user_id,
    2 AS current_dept_id,
    ARRAY(1, 2, 3) AS allowed_dept_ids
  UNION ALL
  SELECT 
    'user_002' AS user_id,
    4 AS current_dept_id,
    ARRAY(4, 5) AS allowed_dept_ids
)

SELECT 
  u.user_id,
  u.current_dept_id,
  dept_id AS allowed_dept_id
FROM user_configs u
JOIN UNNEST(u.allowed_dept_ids) AS dept_id 
  ON dept_id = u.current_dept_id;

-- Result: only rows where an allowed dept matches the current dept
+----------+-----------------+-----------------+
| user_id  | current_dept_id | allowed_dept_id |
+----------+-----------------+-----------------+
| user_001 | 2               | 2               |
| user_002 | 4               | 4               |
+----------+-----------------+-----------------+