Shape / columns · All operations

Spread nested fields into columns

A variant of Parse nested values

Lift every field of a struct or object column into its own top-level column, one row still per row.

Status

In arcform

Runs as a SQL step

arcform carries it as a sql: step today.

The DuckDB step

Spread the fields of a struct into top-level columns, one row per row

It reads the orders table the proofs share. Create it first:

SQL
CREATE TABLE orders AS SELECT * FROM (VALUES
  (1,  'Alice Ng',      'north', DATE '2026-01-03', 120.00, 'widget', 'a|b',   '{"channel":"web","items":[1,2]}'),
  (2,  'alice ng',      'north', DATE '2026-01-05',  80.50, 'gadget', 'b',     '{"channel":"shop","items":[3]}'),
  (3,  'Bob  Okafor',   'south', DATE '2026-01-05', 300.00, 'widget', 'a|c',   '{"channel":"web","items":[4,5,6]}'),
  (4,  'Carla Díaz',    'east',  DATE '2026-01-09',  NULL,  'gizmo',  NULL,    '{"channel":"web","items":[]}'),
  (5,  'Bob Okafor',    'south', DATE '2026-01-12',  45.25, 'gadget', 'c',     '{"channel":"phone","items":[7]}'),
  (6,  'Dev Patel',     'west',  DATE '2026-01-12', 510.00, 'widget', 'a',     '{"channel":"web","items":[8,9]}'),
  (7,  'Carla Diaz',    'east',  DATE '2026-01-20',  99.99, 'gizmo',  'b|c',   '{"channel":"shop","items":[10]}'),
  (8,  'Dev Patel',     'west',  DATE '2026-02-02', 510.00, 'widget', 'a',     '{"channel":"web","items":[8,9]}'),
  (9,  'Eve Larsen',    NULL,    DATE '2026-02-03',  15.00, 'gadget', NULL,    '{"channel":"web","items":[11]}'),
  (10, 'Alice Ng',      'north', DATE '2026-02-10', 220.00, 'gizmo',  'a|b|c', '{"channel":"phone","items":[12,13]}')
) t(order_id, customer, region, order_date, amount, product, tags, meta);

Then run the statement:

SQL
CREATE TEMP TABLE raw AS
SELECT order_id, meta FROM orders UNION ALL SELECT 11, NULL;
CREATE TEMP TABLE result AS
SELECT order_id, s.* FROM (SELECT order_id, from_json(meta, '{"channel":"VARCHAR","items":["INTEGER"]}') AS s FROM raw);
SELECT * FROM result ORDER BY order_id;
DESCRIBE result;
SELECT CASE WHEN ((SELECT list(column_name) FROM (DESCRIBE result)) = ['order_id', 'channel', 'items'] AND (SELECT count(*) FROM result) = 11 AND (SELECT items FROM result WHERE order_id = 3) = [4, 5, 6] AND (SELECT count(*) FROM result WHERE order_id = 11 AND channel IS NULL AND items IS NULL) = 1) THEN 'as expected' ELSE error('the struct was not spread into a channel column and an items column with one row per row, or a missing struct lost its row') END AS verdict;
  • It needs nothing installed beyond DuckDB itself.
  • The statement checks its own result and raises an error when it is wrong: “the struct was not spread into a channel column and an items column with one row per row, or a missing struct lost its row”.
  • It passed on DuckDB v2.0.0 (Cyanoptera) d54cbbe3df.

What other tools call it

Meridian's classification; an independent blind rating agreed on 95.7% of 324 rows (variant, version 3).

Alteryx Designer (1)
  • XML Parseread 2026-09-27

    Parse XML text into individual fields

Apache DataFusion (1)
  • unnest (struct)read 2026-09-27

    Expand the fields of a struct into individual columns

Apache NiFi (1)
  • FlattenJsonread 2026-09-27

    Flatten a nested JSON document into single-level keys joined by a separator

AWS Glue (4)
  • Flattenread 2026-09-27

    Lift the fields inside structs to top-level fields

  • Simplify_ddb_jsonread 2026-09-27

    Simplify nested columns that are in the DynamoDB JSON structure

  • UnnestFrameread 2026-09-27

    Flatten nested objects to top-level fields and generate join keys for array objects

  • unnest_ddb_jsonread 2026-09-27

    Flatten nested columns that are in the DynamoDB JSON structure

Duckle (1)
  • Flattenread 2026-09-25

    flatten nested JSON into columns

KNIME Analytics Platform (1)
  • JSON to Tableread 2026-09-27

    Flatten a JSON column into columns by inferring its structure

Polars (1)
  • DataFrame.unnestread 2026-09-27

    Spread the fields of struct columns into separate columns

Power Query M (1)
Talend (1)
Trifacta (2)
  • Unnest Transformread 2026-09-27

    Unpack keys of an Object or elements of an Array into new columns

  • Unnest elementsread 2026-09-27

    Extract elements of an Object or Array into columns

VisiData (8)
  • contract-colread 2026-09-23

    Remove expanded child columns and restore the parent column

  • contract-col-depthread 2026-09-23

    Contract current column's expansion to a given depth

  • contract-colsread 2026-09-23

    Remove all expanded child columns and restore their parents

  • contract-cols-depthread 2026-09-23

    Contract all expansions to a given depth

  • expand-colread 2026-09-23

    Expand current column of lists or dicts into one column per key, one level

  • expand-col-depthread 2026-09-23

    Expand current column of lists or dicts to a given depth

  • expand-colsread 2026-09-23

    Expand all visible columns of lists or dicts one level

  • expand-cols-depthread 2026-09-23

    Expand all visible columns of lists or dicts to a given depth

Background

In DuckDB, unnesting a STRUCT value creates a column for each member, the member key becoming the column name and the member value the column value.[1] In DuckDB, a STRUCT can also be spread with the dot-star shorthand s.*, though the unnest function offers some additional functionality.[1]

When to use it

In DuckDB, unnesting a struct is particularly useful when a prior operation creates a struct of unknown shape, or when a query must handle any keys the struct may have.[2] Check whether a column is a struct or a list before unnesting it, and count rows before and after whenever it is a list. (Meridian's view)

By default, DuckDB's unnest unpacks only the outermost level, and its recursive and max_depth parameters carry the unnesting further down.[1] When DuckDB recursively unnests a STRUCT, STRUCT members are unnested further while LIST members are left packed.[1]

The method

DuckDB's read_json can unpack the records of a JavaScript Object Notation (JSON) file into columns, and with records set to false it returns a single STRUCT column instead.[3] DuckDB's Avro reader turns the entries of a single top-level record into top-level columns.[4]

Sources

  1. [1]Unnesting – DuckDBdocumentation
  2. [2]Struct Data Type – DuckDBdocumentation
  3. [3]Loading JSON – DuckDBdocumentation
  4. [4]Avro Extension – DuckDBdocumentation