17 SQL/JSONファンクションJSON_TABLE
SQL/JSONファンクションjson_table
は、特定のJSONデータを各種SQLデータ・タイプの列に投影します。これを使用して、JSON式の評価結果を新規の仮想表の行および列に分解しますが、これは、インラインのビューであるとみなすこともできます。
この仮想表は、既存のデータベース表に挿入でき、またSQLを使用して(join式など)問合せできます。
json_table
の一般的な使用目的は、JSONデータのビューを作成することです。このようなビューは、任意の表またはビューを使用する場合と同じように使用できます。このため、アプリケーション、ツールおよびプログラマは、JSONまたはJSONパス式の構文を考慮せずにJSONデータを操作できます。
JSONデータに対してビューを定義することにより、実質的にある種のスキーマがそのデータにマップされます。このマッピングは、事後に行われます。つまり、基礎となるJSONデータは、スキーマまたは特定の使用パターンを考慮せずに定義および作成できます。データが最初で、スキーマが後です。
このようなスキーマ(マッピング)により、基礎となる表に格納できるJSON文書の種類に(整形式のJSONデータであること以外の)制約が課されることはありません。ビューでは、ビューを定義するマッピング(スキーマ)に準拠するデータのみが公開されます。ビューを再定義するだけでスキーマを変更でき、基礎となるJSONデータを再編成する必要はありません。
json_table
はSQLのFROM
句で使用します。これは行ソースです。これにより、行パス式(行パターン)によって選択されたJSON値ごとにデータの行が生成されます。
json_table
の呼出しによって作成される行は、暗黙的にこれらの行の生成元の行に水平結合されます。つまり、json_table
によって生成される仮想表を、JSONデータが含まれる表に明示的に結合する必要はありません。
指定されたjson_table
列仕様でFORMAT JSON
を使用する場合、JSONデータを列に投影する際にjson_query
セマンティクスが使用されます。列に指定されるデータ型は、json_query
が返すことができる、VARCHAR2
、CLOB
またはBLOB
のいずれかのSQLデータ型にできます。
json_query
セマンティクスは、次のことを意味します。
-
投影されるJSONデータは常に整形式です。これには、文字列値の非ASCII文字が必要に応じてエスケープされていることも含まれます。たとえば、タブ文字(CHARACTER TABULATION、U+0009)は
\t
としてエスケープされます。 -
json_query
エラー処理が適用されます。 -
ラッパー句を使用して複数のJSON値を配列内の要素として投影できます。
指定されたjson_table
列仕様でFORMAT JSON
を使用しない場合、JSONデータの投影時にjson_value
セマンティクスが使用されます。列に指定されるデータ型は、json_value
が返すことができる、VARCHAR2
、NUMBER
、DATE
、TIMESTAMP
、TIMESTAMP WITH TIME ZONE
、SDO_GEOMETRY
またはCLOB
のいずれかのSQLデータ型になります。json_value
エラー処理が適用されます(ラッパー句は使用できません)。
投影されたデータがJSONオブジェクトまたは配列の場合は通常、FORMAT JSON
を使用します。投影されたデータがJSONスカラーの場合は通常、FORMAT JSON
は使用しないでください。
json_table
の最初の引数はSQL式です。これは、適切にキャストされた表またはビューの列の値、PL/SQL変数、バインド変数のいずれかになります。式の評価の結果は、行パス式を評価するためのコンテキスト項目として使用されます。
json_table
の2番目の引数はSQL/JSON行パス式であり、行を処理するためのオプションのエラー句と(必須の) COLUMNS
句が付加されます。(RETURNING
句はありません。)このパス式は、任意の数のJSON値を対象とすることができます。
行パス式は、生成される仮想表の行のパターンとして機能します。これが、SQLのFROM
句によって提供されるコンテキスト項目と照合されることにより、COLUMNS
句に指定する列に編成されるSQLデータの行が生成されます。これらの各行が0個以上の列パス式と照合され、仮想表の列が生成されます。
json_table
のエラー処理には2つのレベルがありますが、これらは、パス式の2つのレベルである行と列に対応しています。存在する場合、列のエラー・ハンドラにより、行レベルのエラー処理がオーバーライドされます。両レベルのデフォルトのエラー・ハンドラはNULL ON ERROR
です。
必須のCOLUMNS
句は、json_table
によって作成される仮想表の列を定義します。これは、キーワードCOLUMNS
の後ろに次のエントリをカッコで囲んだ形式で構成されます。
-
COLUMNS
句内の多くても1つのエントリは、生成される行数の列(SQLデータ型NUMBER
)を指定するキーワードFOR ORDINALITY
を列名の後ろに付けた形式にすることができます。これらの数は1から始まります。 -
オプションの
FOR ORDINALITY
エントリ以外、COLUMNS
句の各エントリは、標準列指定またはネストした列指定です。 -
標準列指定は、オプションで列のスカラー・データ型が付いた列名で構成されます。これには、
json_value
のRETURNING
句と同様に、SQLデータ型VARCHAR2
、NUMBER
、DATE
、TIMESTAMP
、TIMESTAMP WITH TIME ZONE
またはSDO_GEOMETRY
を使用でき、後ろにオプションの値句および必須のPATH
句が付けられます。デフォルトのデータ型はVARCHAR2(4000)
です。データ型
SDO_GEOMETRY
は、Oracle Spatial and Graphデータ向けに使用されます。特に、これは、json_table
をGeoJSONデータで使用できることを意味します。このデータは、JSONで地理データをエンコーディングするための形式です。 -
ネストした列指定は、キーワード
NESTED
の後ろにオプションのPATH
キーワード、SQL/JSON行パス式、およびCOLUMNS
句が付いた形式で構成されます。このCOLUMNS
句は、ネストしたデータを表す列を指定します。ここで使用される行パス式により、指定したネストした列のコンテキストが洗練されます。ネストした各列のパス式は行パス式を基準にしたものになります。COLUMNS
句は(ネストしていてもネストしていなくても)どのレベルでも同じ特性を持ちます。つまり、COLUMNS
句は再帰的に定義されます。ネストのレベルごとに(つまり、キーワードNESTED
が使用されるたびに)、ネストしたCOLUMNS
句は、ネスト元のCOLUMNS
句(その親)の子と呼ばれます。同じ親句を持つ複数のCOLUMNS
句は兄弟です。親子の
COLUMNS
句によって定義される仮想表は、外部結合を使用して結合されますが、この場合、親が外部表になります。兄弟のCOLUMNS
句によって定義される仮想列は、和
結合を使用して結合されます。例17-6は、ネストした列句の使用を示しています。
-
オプションの値句は、列に投影されたデータを処理する方法、つまり、データを
json_value
、json_exists
またはjson_query
と同じように処理するかどうかを指定します。この値処理には、戻りデータ型、戻り形式(prettyまたはASCII)、ラッパー、およびエラーの処理が含まれます。デフォルトでは、投影されたデータは
json_value
によって処理されたかのように処理されます。キーワードEXISTS
を使用すると、json_exists
によって処理されたかのように処理されます。キーワードFORMAT JSON
を使用すると、json_query
によって処理されたかのように処理されます。FORMAT JSON
の場合、明示的なラッパー句を追加することにより、デフォルトのラッパー動作をオーバーライドできます。特定のハンドラ(
json_value
、json_exists
またはjson_query
)のデフォルトのエラー処理を、それに適した明示的なエラー句を追加することでオーバーライドできます。 -
必須の
PATH
句は、列の内容として使用される行の部分を指定します。キーワードPATH
に続く列パス式は、仮想行によって提供されるコンテキスト項目と照合されます。この列パス式は、行パス式によって指定されるパスに対して相対的であるため、相対パスの表記にする必要があります。
関連項目:
-
Oracle Spatial and Graphデータの使用の詳細は、『Oracle Spatial and Graph開発者ガイド』を参照してください。
トピック:
- JSON_TABLEによるSQL/JSON問合せファンクションおよび条件の一般化
SQL/JSONファンクションjson_table
は、SQL/JSON条件json_exists
と、SQL/JSONファンクションjson_value
およびjson_query
を一般化します。これらの関数を使用して実行できる処理はすべて、json_table
を使用して実行できます。これらによって実行されるジョブについては、これらの関数の構文の方がjson_table
の構文よりも簡単に使用できます。 - JSON_TABLEとJSON配列の使用
JSON値は、1つの配列にすることも、1つ以上の配列を含めることも可能であり、他のJSON配列またはオブジェクト内の任意の数のレベルにネストしてもかまいません。json_table
NESTED
パス句を使用して、配列の特定の要素を投影できます。 - JSON_TABLEを使用したJSONデータに対するビューの作成
問合せのパフォーマンスを向上させるために、SQL/JSONファンクションjson_table
を使用して、列に投影するJSONデータに対してビューを作成できます。問合せのパフォーマンスをさらに向上させるには、読取り専用のマテリアライズド・ビューを作成し、JSONデータをインメモリーに配置できます。
17.1 JSON_TABLEによるSQL/JSON問合せファンクションおよび条件の一般化
SQL/JSONファンクションjson_table
は、SQL/JSON条件json_exists
と、SQL/JSONファンクションjson_value
およびjson_query
を一般化します。これらの関数を使用して実行できる処理はすべて、json_table
を使用して実行できます。これらによって実行されるジョブについては、これらの関数の構文の方がjson_table
の構文よりも簡単に使用できます。
json_exists
、json_value
またはjson_query
を複数回使用して、またはこれらを組み合せて使用して同じデータにアクセスする場合、json_table
を1回呼び出す方が、データが解析されるのが1回のみであるという利点があります。
このため、オプティマイザがjson_exists
、json_value
およびjson_query
の複数の呼出し(任意の組合せ)を、データの解析が1回だけになるように、より少ないjson_table
の呼出しに自動的にリライトすることがよく起こります。
例17-1および例17-2にこれを示します。これらではそれぞれ、列j_purchaseorder.po_document
内の各オブジェクトによって使用される要求者および一連の電話を選択しています。ただし、例17-2では、この列を4回ではなく1回のみ解析しています。
例17-2と関連して次の点に注意してください。
-
JSON値の
null
は、SQLに関するかぎりは1つの値であり、SQLで値の欠如(存在しないデータ、不明なデータまたは適用できないデータ)を表すNULL
とは異なります。例17-2で、オブジェクト属性zipCode
のJSON値がnull
である場合、SQL文字列'true'
が戻されます。 -
json_exists
によってブール値が戻されますが、これは、SQL値として、SQL文字列'true'
または'false'
によって表されます。json_exists
がSQLのWHERE
句またはCASE
文の条件として直接使用される場合、この戻り値を明示的にテストする必要はありません。単にjson_exists(...)
と入力してかまいません。ただし、json_exists
が値を得るために他の場所で使用される場合、この値をテストする唯一の方法はこの値を明示的な文字列として使用する方法です。これが例17-2の事例です。値は列jt.has_zip
に格納されてから、SQL文字列'true'
と等価かどうかが明示的にテストされています。 -
JSONオブジェクト属性
AllowPartialShipment
には、JSONのブール値があります。この値にjson_value
が適用される場合、これは文字列または数値として戻される場合があります。例17-2では、データ型NUMBER
は列のデータ型として使用されます。関数json_table
は、暗黙的にこの列に対してjson_value
を使用し、値を数値として戻します。この値の数値1
に対する等価性がテストされます。
例17-1 JSONデータに複数回アクセスすることによるデータの抽出
SELECT json_value(po_document, '$.Requestor' RETURNING VARCHAR2(32)),
json_query(po_document, '$.ShippingInstructions.Phone'
RETURNING VARCHAR2(100))
FROM j_purchaseorder
WHERE json_exists(po_document, '$.ShippingInstructions.Address.zipCode')
AND json_value(po_document, '$.AllowPartialShipment' RETURNING NUMBER(1))
= 1;
例17-2 JSON_TABLEの使用によって複数回の解析が不要なデータの抽出
SELECT jt.requestor, jt.phones
FROM j_purchaseorder,
json_table(po_document, '$'
COLUMNS (requestor VARCHAR2(32 CHAR) PATH '$.Requestor',
phones VARCHAR2(100 CHAR) FORMAT JSON
PATH '$.ShippingInstructions.Phone',
partial NUMBER(1) PATH '$.AllowPartialShipment',
has_zip VARCHAR2(5 CHAR) EXISTS
PATH '$.ShippingInstructions.Address.zipCode')) jt
WHERE jt.partial = 1 AND has_zip = 'true';
17.2 JSON_TABLEとJSON配列の使用
JSON値は、1つの配列にすることも、1つ以上の配列を含めることも可能であり、他のJSON配列またはオブジェクト内の任意の数のレベルにネストしてもかまいません。json_table
NESTED
パス句を使用して、配列の特定の要素を投影できます。
例17-3では、JSONデータ内の要求者および関連する電話番号を列po_document
内に投影しています。JSON配列Phone
全体がJSONデータph_arr
の列として投影されています。このJSONデータをVARCHAR2
列としてフォーマットするには、キーワードFORMAT JSON
が必要です。
JSON配列Phone
全体ではなく、配列の個別要素のみを投影する場合はどうすればよいでしょうか。例17-4は、これを行う方法の1つを示しており、投影する必要があるデータが配列要素のみである場合、この方法を使用できます。
要求者と関連する電話データの両方を投影する場合、例17-4の行パス式($.Phone[*]
)は適切ではありません。この式は、配列Phone
の(電話オブジェクト)要素のみを対象としています。
例17-5は、両方を対象とする方法の1つを示しています。ここでは、名前と電話配列全体の両方を対象とする行パス式を使用するとともに、個別電話オブジェクトのフィールドtype
およびnumber
を対象とする列パス式を使用しています。
例17-5では例17-3の場合のようにキーワードFORMAT JSON
が必要ですが、これは、生成される列VARCHAR2
にJSONデータ(つまり、電話ごとに1つの配列要素という形式による電話のタイプまたは電話番号の配列)が含まれるためです。また、例17-3の事例とは異なり、列phone_type
およびphone_num
にラッパー句が必要ですが、これは、フィールドtype
およびnumber
を持つオブジェクトが配列Phone
に複数含まれるためです。
場合によっては、例17-5の効果が必要ないことがあります。たとえば、電話番号のJSON配列が含まれる列(特定の発注書のすべての番号に対して1つの行)ではなく、単一の電話番号が含まれるリレーショナル列(番号当たり1つの行)が必要な場合があります。
この結果を得るために、配列に対してjson_table
のNESTED
パス句を使用することにより、配列要素を投影するようjson_table
に命令を出す必要があります。NESTED
パス句は実質的に、追加の行ソース(行パターン)として機能します。例17-6に、これを示します。
キーワードNESTED
は1回のjson_table
の呼出しで何回でも使用できます。
例17-6では、外部のCOLUMNS
句は、ネストした(内部の) COLUMNS
句の親です。定義されている仮想表は外部結合を使用して結合されますが、この場合、親句によって定義される表が結合における外部表になります。
(同じ親の下で直接ネストされた2番目の列の句がある場合、これら2つのネストした句は兄弟のCOLUMNS
句になります。)
例17-3 JSON配列全体のJSONデータとしての投影
SELECT jt.*
FROM j_purchaseorder,
json_table(po_document, '$'
COLUMNS (requestor VARCHAR2(32 CHAR) PATH '$.Requestor',
ph_arr VARCHAR2(100 CHAR) FORMAT JSON
PATH '$.ShippingInstructions.Phone')) AS "JT";
例17-4 JSON配列の要素の投影
SELECT jt.*
FROM j_purchaseorder,
json_table(po_document, '$.ShippingInstructions.Phone[*]'
COLUMNS (phone_type VARCHAR2(10) PATH '$.type',
phone_num VARCHAR2(20) PATH '$.number')) AS "JT";
PHONE_TYPE PHONE_NUM
---------- ---------
Office 909-555-7307
Mobile 415-555-1234
例17-5 JSON配列の要素と他のデータの投影
SELECT jt.*
FROM j_purchaseorder,
json_table(po_document, '$'
COLUMNS (
requestor VARCHAR2(32 CHAR) PATH '$.Requestor',
phone_type VARCHAR2(50 CHAR) FORMAT JSON WITH WRAPPER
PATH '$.ShippingInstructions.Phone[*].type',
phone_num VARCHAR2(50 CHAR) FORMAT JSON WITH WRAPPER
PATH '$.ShippingInstructions.Phone[*].number')) AS "JT";
REQUESTOR PHONE_TYPE PHONE_NUM
--------- ---------- ---------
Alexis Bull ["Office", "Mobile"] ["909-555-7307", "415-555-1234"]
例17-6 JSON_TABLE: NESTEDを使用した配列要素の投影
SELECT jt.*
FROM j_purchaseorder,
json_table(po_document, '$'
COLUMNS (
requestor VARCHAR2(32 CHAR) PATH '$.Requestor',
NESTED PATH '$.ShippingInstructions.Phone[*]'
COLUMNS (phone_type VARCHAR2(32 CHAR) PATH '$.type',
phone_num VARCHAR2(20 CHAR) PATH '$.number'))) AS "JT";
17.3 JSON_TABLEを使用したJSONデータに対するビューの作成
問合せのパフォーマンスを向上させるために、SQL/JSONファンクションjson_table
を使用して、列に投影するJSONデータに対してビューを作成できます。問合せのパフォーマンスをさらに向上させるには、読取り専用のマテリアライズド・ビューを作成し、JSONデータをインメモリーに配置できます。
例17-7では、JSONデータに対してビューを定義しています。ここでは、NESTED
パス句を使用して配列LineItems
の要素を投影しています。
例17-8では、例17-7と同じデータおよび構造を持つマテリアライズド・ビューを定義します。このようなマテリアライズド・ビューは更新用には使用できません。読取り専用のビューとして扱う必要があります。変更を試みると、エラーが発生します。
-
キーワード
MATERIALIZED
の使用 -
BUILD IMMEDIATE
の使用 -
REFRESH FAST ON COMMIT WITH PRIMARY KEY
の使用
REFRESH FAST
の使用は、マテリアライズド・ビューが増分的にリフレッシュされることを意味します。これが発生するようにするには、WITH PRIMARY KEY
またはWITH ROWID
(主キーがない場合)を使用する必要があります。表に基づいてマテリアライズド・ビューを作成する際には、JSON列が含まれるベースとなる表に主キーを指定し、WITH PRIMARY KEY
を使用することをお薦めします。
例17-7 JSONデータに対するビューの作成
CREATE OR REPLACE VIEW j_purchaseorder_detail_view
AS SELECT d.*
FROM j_purchaseorder po,
json_table(po.po_document, '$'
COLUMNS (
po_number NUMBER(10) PATH '$.PONumber',
reference VARCHAR2(30 CHAR) PATH '$.Reference',
requestor VARCHAR2(128 CHAR) PATH '$.Requestor',
userid VARCHAR2(10 CHAR) PATH '$.User',
costcenter VARCHAR2(16) PATH '$.CostCenter',
ship_to_name VARCHAR2(20 CHAR)
PATH '$.ShippingInstructions.name',
ship_to_street VARCHAR2(32 CHAR)
PATH '$.ShippingInstructions.Address.street',
ship_to_city VARCHAR2(32 CHAR)
PATH '$.ShippingInstructions.Address.city',
ship_to_county VARCHAR2(32 CHAR)
PATH '$.ShippingInstructions.Address.county',
ship_to_postcode VARCHAR2(10 CHAR)
PATH '$.ShippingInstructions.Address.postcode',
ship_to_state VARCHAR2(2 CHAR)
PATH '$.ShippingInstructions.Address.state',
ship_to_zip VARCHAR2(8 CHAR)
PATH '$.ShippingInstructions.Address.zipCode',
ship_to_country VARCHAR2(32 CHAR)
PATH '$.ShippingInstructions.Address.country',
ship_to_phone VARCHAR2(24 CHAR)
PATH '$.ShippingInstructions.Phone[0].number',
NESTED PATH '$.LineItems[*]'
COLUMNS (
itemno NUMBER(38) PATH '$.ItemNumber',
description VARCHAR2(256 CHAR) PATH '$.Part.Description',
upc_code VARCHAR2(14 CHAR) PATH '$.Part.UPCCode',
quantity NUMBER(12,4) PATH '$.Quantity',
unitprice NUMBER(14,2) PATH '$.Part.UnitPrice'))) d;
例17-8 JSONデータに対するマテリアライズド・ビューの作成
CREATE OR REPLACE MATERIALIZED VIEW j_purchaseorder_materialized_view
BUILD IMMEDIATE
REFRESH FAST ON COMMIT WITH PRIMARY KEY
AS SELECT d.*
FROM j_purchaseorder po,
json_table(po.po_document, '$'
COLUMNS (
po_number NUMBER(10) PATH '$.PONumber',
reference VARCHAR2(30 CHAR) PATH '$.Reference',
requestor VARCHAR2(128 CHAR) PATH '$.Requestor',
userid VARCHAR2(10 CHAR) PATH '$.User',
costcenter VARCHAR2(16) PATH '$.CostCenter',
ship_to_name VARCHAR2(20 CHAR)
PATH '$.ShippingInstructions.name',
ship_to_street VARCHAR2(32 CHAR)
PATH '$.ShippingInstructions.Address.street',
ship_to_city VARCHAR2(32 CHAR)
PATH '$.ShippingInstructions.Address.city',
ship_to_county VARCHAR2(32 CHAR)
PATH '$.ShippingInstructions.Address.county',
ship_to_postcode VARCHAR2(10 CHAR)
PATH '$.ShippingInstructions.Address.postcode',
ship_to_state VARCHAR2(2 CHAR)
PATH '$.ShippingInstructions.Address.state',
ship_to_zip VARCHAR2(8 CHAR)
PATH '$.ShippingInstructions.Address.zipCode',
ship_to_country VARCHAR2(32 CHAR)
PATH '$.ShippingInstructions.Address.country',
ship_to_phone VARCHAR2(24 CHAR)
PATH '$.ShippingInstructions.Phone[0].number',
NESTED PATH '$.LineItems[*]'
COLUMNS (
itemno NUMBER(38) PATH '$.ItemNumber',
description VARCHAR2(256 CHAR) PATH '$.Part.Description',
upc_code VARCHAR2(14 CHAR) PATH '$.Part.UPCCode',
quantity NUMBER(12,4) PATH '$.Quantity',
unitprice NUMBER(14,2) PATH '$.Part.UnitPrice'))) d;
関連項目