@@ -18893,6 +18893,24 @@ DETAIL: Missing "]" after array dimensions.
18893
18893
row.
18894
18894
</para>
18895
18895
18896
+ <para>
18897
+ JSON data stored at a nested level of the row pattern can be extracted using
18898
+ the <literal>NESTED PATH</literal> clause. Each
18899
+ <literal>NESTED PATH</literal> clause can be used to generate one or more
18900
+ columns using the data from a nested level of the row pattern. Those
18901
+ columns can be specified using a <literal>COLUMNS</literal> clause that
18902
+ looks similar to the top-level COLUMNS clause. Rows constructed from
18903
+ NESTED COLUMNS are called <firstterm>child rows</firstterm> and are joined
18904
+ against the row constructed from the columns specified in the parent
18905
+ <literal>COLUMNS</literal> clause to get the row in the final view. Child
18906
+ columns themselves may contain a <literal>NESTED PATH</literal>
18907
+ specification thus allowing to extract data located at arbitrary nesting
18908
+ levels. Columns produced by multiple <literal>NESTED PATH</literal>s at the
18909
+ same level are considered to be <firstterm>siblings</firstterm> of each
18910
+ other and their rows after joining with the parent row are combined using
18911
+ UNION.
18912
+ </para>
18913
+
18896
18914
<para>
18897
18915
The rows produced by <function>JSON_TABLE</function> are laterally
18898
18916
joined to the row that generated them, so you do not have to explicitly join
@@ -18924,6 +18942,7 @@ where <replaceable class="parameter">json_table_column</replaceable> is:
18924
18942
<optional> { ERROR | NULL | EMPTY { ARRAY | OBJECT } | DEFAULT <replaceable>expression</replaceable> } ON ERROR </optional>
18925
18943
| <replaceable>name</replaceable> <replaceable>type</replaceable> EXISTS <optional> PATH <replaceable>path_expression</replaceable> </optional>
18926
18944
<optional> { ERROR | TRUE | FALSE | UNKNOWN } ON ERROR </optional>
18945
+ | NESTED <optional> PATH </optional> <replaceable>json_path_specification</replaceable> <optional> AS <replaceable>json_path_name</replaceable> </optional> COLUMNS ( <replaceable>json_table_column</replaceable> <optional>, ...</optional> )
18927
18946
</synopsis>
18928
18947
18929
18948
<para>
@@ -18971,7 +18990,8 @@ where <replaceable class="parameter">json_table_column</replaceable> is:
18971
18990
<listitem>
18972
18991
<para>
18973
18992
Adds an ordinality column that provides sequential row numbering starting
18974
- from 1.
18993
+ from 1. Each <literal>NESTED PATH</literal> (see below) gets its own
18994
+ counter for any nested ordinality columns.
18975
18995
</para>
18976
18996
</listitem>
18977
18997
</varlistentry>
@@ -19060,6 +19080,33 @@ where <replaceable class="parameter">json_table_column</replaceable> is:
19060
19080
</note>
19061
19081
</listitem>
19062
19082
</varlistentry>
19083
+
19084
+ <varlistentry>
19085
+ <term>
19086
+ <literal>NESTED <optional> PATH </optional></literal> <replaceable>json_path_specification</replaceable> <optional> <literal>AS</literal> <replaceable>json_path_name</replaceable> </optional>
19087
+ <literal>COLUMNS</literal> ( <replaceable>json_table_column</replaceable> <optional>, ...</optional> )
19088
+ </term>
19089
+ <listitem>
19090
+
19091
+ <para>
19092
+ Extracts SQL/JSON values from nested levels of the row pattern,
19093
+ generates one or more columns as defined by the <literal>COLUMNS</literal>
19094
+ subclause, and inserts the extracted SQL/JSON values into those
19095
+ columns. The <replaceable>json_table_column</replaceable>
19096
+ expression in the <literal>COLUMNS</literal> subclause uses the same
19097
+ syntax as in the parent <literal>COLUMNS</literal> clause.
19098
+ </para>
19099
+
19100
+ <para>
19101
+ The <literal>NESTED PATH</literal> syntax is recursive,
19102
+ so you can go down multiple nested levels by specifying several
19103
+ <literal>NESTED PATH</literal> subclauses within each other.
19104
+ It allows to unnest the hierarchy of JSON objects and arrays
19105
+ in a single function invocation rather than chaining several
19106
+ <function>JSON_TABLE</function> expressions in an SQL statement.
19107
+ </para>
19108
+ </listitem>
19109
+ </varlistentry>
19063
19110
</variablelist>
19064
19111
19065
19112
<note>
@@ -19189,6 +19236,111 @@ SELECT jt.* FROM
19189
19236
1 | horror | Psycho | "Alfred Hitchcock"
19190
19237
2 | thriller | Vertigo | "Alfred Hitchcock"
19191
19238
(2 rows)
19239
+ </screen>
19240
+
19241
+ </para>
19242
+ <para>
19243
+ The following is a modified version of the above query to show the usage
19244
+ of <literal>NESTED PATH</literal> for populating title and director
19245
+ columns, illustrating how they are joined to the parent columns id and
19246
+ kind:
19247
+
19248
+ <programlisting>
19249
+ SELECT jt.* FROM
19250
+ my_films,
19251
+ JSON_TABLE ( js, '$.favorites[*] ? (@.films[*].director == $filter)'
19252
+ PASSING 'Alfred Hitchcock' AS filter
19253
+ COLUMNS (
19254
+ id FOR ORDINALITY,
19255
+ kind text PATH '$.kind',
19256
+ NESTED PATH '$.films[*]' COLUMNS (
19257
+ title text FORMAT JSON PATH '$.title' OMIT QUOTES,
19258
+ director text PATH '$.director' KEEP QUOTES))) AS jt;
19259
+ </programlisting>
19260
+
19261
+ <screen>
19262
+ id | kind | title | director
19263
+ ----+----------+---------+--------------------
19264
+ 1 | horror | Psycho | "Alfred Hitchcock"
19265
+ 2 | thriller | Vertigo | "Alfred Hitchcock"
19266
+ (2 rows)
19267
+ </screen>
19268
+
19269
+ </para>
19270
+
19271
+ <para>
19272
+ The following is the same query but without the filter in the root
19273
+ path:
19274
+
19275
+ <programlisting>
19276
+ SELECT jt.* FROM
19277
+ my_films,
19278
+ JSON_TABLE ( js, '$.favorites[*]'
19279
+ COLUMNS (
19280
+ id FOR ORDINALITY,
19281
+ kind text PATH '$.kind',
19282
+ NESTED PATH '$.films[*]' COLUMNS (
19283
+ title text FORMAT JSON PATH '$.title' OMIT QUOTES,
19284
+ director text PATH '$.director' KEEP QUOTES))) AS jt;
19285
+ </programlisting>
19286
+
19287
+ <screen>
19288
+ id | kind | title | director
19289
+ ----+----------+-----------------+--------------------
19290
+ 1 | comedy | Bananas | "Woody Allen"
19291
+ 1 | comedy | The Dinner Game | "Francis Veber"
19292
+ 2 | horror | Psycho | "Alfred Hitchcock"
19293
+ 3 | thriller | Vertigo | "Alfred Hitchcock"
19294
+ 4 | drama | Yojimbo | "Akira Kurosawa"
19295
+ (5 rows)
19296
+ </screen>
19297
+
19298
+ </para>
19299
+
19300
+ <para>
19301
+ The following shows another query using a different <type>JSON</type>
19302
+ object as input. It shows the UNION "sibling join" between
19303
+ <literal>NESTED</literal> paths <literal>$.movies[*]</literal> and
19304
+ <literal>$.books[*]</literal> and also the usage of
19305
+ <literal>FOR ORDINALITY</literal> column at <literal>NESTED</literal>
19306
+ levels (columns <literal>movie_id</literal>, <literal>book_id</literal>,
19307
+ and <literal>author_id</literal>):
19308
+
19309
+ <programlisting>
19310
+ SELECT * FROM JSON_TABLE (
19311
+ '{"favorites":
19312
+ {"movies":
19313
+ [{"name": "One", "director": "John Doe"},
19314
+ {"name": "Two", "director": "Don Joe"}],
19315
+ "books":
19316
+ [{"name": "Mystery", "authors": [{"name": "Brown Dan"}]},
19317
+ {"name": "Wonder", "authors": [{"name": "Jun Murakami"}, {"name":"Craig Doe"}]}]
19318
+ }}'::json, '$.favs[*]'
19319
+ COLUMNS (user_id FOR ORDINALITY,
19320
+ NESTED '$.movies[*]'
19321
+ COLUMNS (
19322
+ movie_id FOR ORDINALITY,
19323
+ mname text PATH '$.name',
19324
+ director text),
19325
+ NESTED '$.books[*]'
19326
+ COLUMNS (
19327
+ book_id FOR ORDINALITY,
19328
+ bname text PATH '$.name',
19329
+ NESTED '$.authors[*]'
19330
+ COLUMNS (
19331
+ author_id FOR ORDINALITY,
19332
+ author_name text PATH '$.name'))));
19333
+ </programlisting>
19334
+
19335
+ <screen>
19336
+ user_id | movie_id | mname | director | book_id | bname | author_id | author_name
19337
+ ---------+----------+-------+----------+---------+---------+-----------+--------------
19338
+ 1 | 1 | One | John Doe | | | |
19339
+ 1 | 2 | Two | Don Joe | | | |
19340
+ 1 | | | | 1 | Mystery | 1 | Brown Dan
19341
+ 1 | | | | 2 | Wonder | 1 | Jun Murakami
19342
+ 1 | | | | 2 | Wonder | 2 | Craig Doe
19343
+ (5 rows)
19192
19344
</screen>
19193
19345
19194
19346
</para>
0 commit comments