Semi-structured & modern warehouse SQL
Concept  ·  SQL & Querying

Structs are typed nesting — a missing field is a compile-time binder error; JSON is schemaless — a missing key is a runtime NULL.

The same dot-shaped access hides two contracts. A struct column's shape is part of the table's type: the engine knows every field at plan time, so typos fail loudly, before data — the binder error naming candidates is the schema working for you. JSON defers to runtime: flexible for drift, silent for typos (payload.tempreture is NULL forever). Hence: JSON at the edges, structs in the modeled core, an explicit promotion step between.

See it break
ping
id
1
The trap
SELECT
  id,
  {'depth': 4}.temp AS tempFROM
  ping;
The fix
SELECT
  id,
  {'depth': 4}.depth AS depth,
  json_extract('{"depth":4}', '$.temp') AS json_temp
FROM
  ping;

Accessing a missing struct field raised a binder error before any data ran — a struct is typed nesting, so a typo fails at compile time; the same missing key in JSON returns a runtime NULL, silent forever.

The rule

JSON at edges, structs in the core, an explicit promotion step; struct typos are free tests — compile the query.

Shows up elsewhere
JSON null vs missing keyJSON extraction
Try one →Prove it in practice →