29 JSONデータの索引
関数ベースの索引を使用すると、JSONデータのスカラー値を索引付けできます。また、JSON検索索引を定義できます。これは、非定型の構造的問合せと全文問合せの両方に便利です。
- JSONデータの索引付けの概要
関数ベースの索引を使用して、JSONデータ内の特定のスカラー値を索引付けできます。非定型の構造的問合せおよび全文問合せ用に、JSON検索索引を使用して通常の方法でJSONデータを索引付けできます。 - JSONデータに対して関数ベースの索引が選択されるかどうかを確認する方法
特定の問合せに対して特定の索引が選択されるかどうかは、オプティマイザによって決定されます。特定の問合せで特定の関数ベースの索引が選択されるかどうかを確認するには、問合せの実行計画内で索引名を検索します。 - JSON_VALUEのためのビットマップ索引の作成
SQL/JSONファンクションjson_value
のためにビットマップ索引を作成できます。ビットマップ索引は、問合せが小さいセットのJSON値のみを対象にしている場合に適しています。 - JSON_VALUEのためのBツリー索引の作成
SQL/JSONファンクションjson_value
のために、Bツリーの関数ベースの索引を作成できます。これを行うには、標準構文を使用してjson_value
を明示的に指定するか、項目メソッドを持つドット表記法構文を使用できます。これらいずれかの方法で作成された索引は、ドット表記法問合せとjson_value
問合せのどちらとも一緒に使用できます。 - JSON_VALUE関数ベースの索引とJSON_TABLE問合せの使用
ERROR ON ERROR
を指定したjson_value
を使用して作成された索引は、json_table
を含む問合せに使用できます。この場合、この索引が索引付きパスに対する制約として機能することで、JSONデータ内の項目ごとに必ず1つの(非null
)スカラーJSON値のみが投影されるようになります。 - JSON_VALUE関数ベースの索引とJSON_EXISTS問合せの使用
ERROR ON ERROR
を指定したSQL/JSONファンクションjson_value
を使用して作成された索引は、SQL/JSON条件json_exists
を含む問合せに使用できます。 - JSON_VALUEの索引付けおよび問合せに関するデータ型の考慮事項
SQL/JSONファンクションjson_value
を使用して作成されたファンクション・ベースの索引では、問合せでjson_value
によって返されたデータ型が索引で指定された型と一致する必要があります。 - JSON_EXISTSの複数値関数ベースの索引の作成
JSON
データ型として格納されるJSONデータの場合、SQL/JSON条件json_exists
に複数値関数ベースの索引を使用できます。このような索引は、スカラーJSON値を個別に、またはJSON配列内で対象とします。 - 複数値関数ベースの索引の使用
WHERE
句のjson_exists
問合せでは、対象とするデータが索引に指定されたスカラー型と一致する場合(かつその場合にかぎり)、複数値関数ベースの索引を選択できます。 - コンポジットBツリー索引を使用した複数のJSONフィールドの索引付け
JSONオブジェクトの複数のフィールドに索引を付けるには、SQL/JSONファンクションjson_value
またはドット表記法構文で複数のパス式を使用してコンポジットBツリー索引を作成できます。 - 非定型の問合せおよび全文検索のためのJSON検索索引
JSON検索索引は、一般的な索引です。この検索索引によって、(1)非定型の構造的問合せ、つまり、定期的には予測または使用されない可能性のある問合せと、(2)全文検索の両方のパフォーマンスを向上させることができます。これは、JSONデータでの使用に特化して設計されたOracle Text索引です。
親トピック: JSON用のパフォーマンス・チューニング
29.1 JSONデータの索引付けの概要
関数ベースの索引を使用して、JSONデータ内の特定のスカラー値を索引付けできます。非定型の構造的問合せおよび全文問合せ用に、JSON検索索引を使用して通常の方法でJSONデータを索引付けできます。
同様に、関数ベースの索引付けは、特定の関数を対象とする問合せに適していますが、これは、SQL/JSONファンクションのコンテキストでは特定のSQL/JSONパス式を意味します。この索引付けは、非定型(つまり、任意)の問合せの場合はあまり役に立ちません。関数ベースの索引を定義するのは、特定のパス式を何度も問い合せることがわかっている場合にしてください。
JSONデータの格納に使用するSQLデータ型に関係なく、SQL/JSONファンクションjson_value
問合せにBツリーまたはビットマップ関数ベースの索引を使用できます。このような索引は、単一のスカラーJSON値を対象とします。関数で想定される値の数が少ない場合は、常にビットマップ索引が適しています。たとえば、対象となる値が少数であると予想される場合は、json_value
のためにビットマップ索引を使用できます。
JSON
型として格納されるJSONデータの場合、SQL/JSON条件json_exists
に複数値関数ベースの索引を使用できます。このような索引は、スカラーJSON値を個別に、または(特に)JSON配列の要素として対象にします。
複数値索引は単一のスカラー値に索引付けできますが、このような値を対象とするパス式が予想される場合は、Bツリー索引またはビットマップ索引を使用する方がパフォーマンスが高くなります。特に、スカラー値の配列を対象にする予定のパス式に索引を付けるには、複数値索引を使用します。
フィルタ式が含まれたSQL/JSONパス式は、ファンクション・ベースの索引を選択する問合せで使用できます。ただし、ファンクション・ベースの索引を定義するために使用するパス式には述語を含めることができません。
非定型方式で問い合せる場合は、JSON検索索引を定義してください。これは、一般的索引であり、特定のパス式の対象ではありません。これは、構造問合せ(特定の値を持つJSONフィールドを検索する場合など)や、Oracle SQL条件json_textcontains
を使用した全文問合せ(様々な文字列値から特定の単語を検索する場合など)に適しています。
同じJSON列に対して関数ベースの索引とJSON検索索引を定義することもできます。
JSON検索索引は、JSONデータでの使用に特化して設計されたOracle Text (全文)索引です。
ノート:
データベース文字セットとしてAL32UTF8を使用することをお薦めします。索引の作成または適用時に、文字セットの自動変換が行われる場合があります。このような変換では情報が失われる可能性があります。これは、問合せで戻されることが予測されるデータが、戻されない場合があることを意味します。JSONデータの文字セットおよび文字エンコーディングを参照してください。
静的ディクショナリ・ビューDBA_JSON_INDEXES
、ALL_JSON_INDEXES
およびUSER_JSON_INDEXES
では、それぞれ、データベース内のJSONデータに対するすべての索引、それらのうち現在のユーザーがアクセスできるすべて、およびそれらのうち現在のユーザーが所有しているすべてが示されます。
コンポジット索引の場合、静的ディクショナリ・ビューDBA_TABLE_VIRTUAL_COLUMNS
、ALL_TABLE_VIRTUAL_COLUMNS
およびUSER_TABLE_VIRTUAL_COLUMNS
が、索引付け用に自動的に作成される仮想列に関する情報を提供します。*_JSON_INDEXES
ビューを補足します。
29.2 JSONデータに対して関数ベースの索引が選択されるかどうかを確認する方法
指定された問合せに対して特定の索引が選択されるかどうかは、オプティマイザによって決定されます。特定の問合せで特定の関数ベースの索引が選択されるかどうかを確認するには、問合せの実行計画内で索引名を検索します。
たとえば:
-
例29-3で定義した索引が指定されると、例29-5、例29-6、例29-7、例29-8および例29-10の例の各問合せの実行計画は、索引
po_num_id1
によって索引スキャンを参照します。 -
例29-14で定義した索引が指定されると、例29-17および例29-18の問合せの実行計画は、索引
mvi_1
によって索引スキャンを参照します。
複数値索引が選択された場合、索引範囲スキャンの実行計画には(MULTI VALUE)
も表示され、計画に使用されるフィルタはJSON_EXISTS2
ではなくJSON_QUERY
です。実行計画に、指定されたjson_exists
問合せに対する複数値索引が使用されない場合、フィルタはJSON_EXISTS2
になります。
29.3 JSON_VALUEのためのビットマップ索引の作成
SQL/JSONファンクションjson_value
に対してビットマップ索引を作成できます。ビットマップ索引は、問合せが小さいセットのJSON値のみを対象にしている場合に適しています。
例29-1 JSON_VALUEのビットマップ索引の作成
データ内のフィールドCostCenter
に対して想定される値が少ない場合、これは作成に適した索引です。
CREATE BITMAP INDEX cost_ctr_idx ON j_purchaseorder
(json_value(po_document, '$.CostCenter'));
親トピック: JSONデータの索引
29.4 JSON_VALUEのためのBツリー索引の作成
SQL/JSONファンクションjson_value
のために、Bツリーの関数ベースの索引を作成できます。これを行うには、標準構文を使用してjson_value
を明示的に指定するか、項目メソッドを持つドット表記法構文を使用できます。これらいずれかの方法で作成された索引は、ドット表記法問合せとjson_value
問合せのどちらとも一緒に使用できます。
例29-3では、表j_purchaseorder
の列po_document
内にあるオブジェクトのフィールドPONumber
に対するjson_value
のファンクション・ベースの索引を作成しています。オブジェクトはパス式のコンテキスト項目として渡されます。
ここでERROR ON ERROR
を使用することは、フィールドPONumber
がないレコード、複数のPONumber
フィールドを持つレコードまたは非数値の値を持つPONumber
フィールドがあるレコードがデータに含まれる場合、索引の作成が失敗することを意味します。また、索引が存在する場合、このようなレコードを挿入しようとしても失敗します。
代替の方法としては、JSONデータに対する単純なドット表記法アクセスで説明されているように、ドット表記法構文を使用して対象となるデータに項目メソッドを適用して索引を作成する方法があります。例29-2に、これを示します。
例29-3と例29-2の両方で作成した索引は、ドット表記法構文を使用する問合せとjson_value
を使用する問合せのいずれかに対して選択できます。
json_value
式の対象のフィールドで欠落している可能性のあるデータの索引付けを許可する場合は、NULL ON EMPTY
句をERROR ON ERROR
句と一緒に使用します。例29-4に、これを示します。
次のいずれかの形式を使用してjson_value
に対して関数ベースの索引を作成することをお薦めします。どちらの場合も、指定されたJSONデータ型のスカラーの結果となるドット表記法およびjson_value
問合せの両方で、索引を使用できます。
-
項目メソッドが索引付けされる値に適用されるドット表記法構文。索引付けされる値は、項目メソッドで指定されたデータ型のスカラーのみです。
-
RETURNING
のデータ型を指定するjson_value
式。必要に応じて、ERROR ON ERROR
およびNULL ON EMPTY
を使用できます。索引付けされる値は、RETURNING
句で指定されたデータ型のスカラーのみです。
このように、これらいずれかの方法で作成された索引は、ドット表記法問合せとjson_value
問合せのどちらとも一緒に使用できます。
関連項目:
Oracle Database SQL言語リファレンスのCREATE INDEX
例29-2 JSONフィールドに対するファンクション・ベースの索引の作成: ドット表記法
項目メソッドnumber()
を使用すると、数値型の索引が作成されます。ドット表記法を使用して関数ベースの索引を作成する場合は、対象データに項目メソッドを常に適用します。
CREATE UNIQUE INDEX po_num_idx1 ON j_purchaseorder po
(po.po_document.PONumber.number());
ノート:
デフォルトでは、関数ベースの索引にはNULL
値は含まれません。索引で使用されるjson_value
式がNULL
を返す場合、デフォルトでは、一致するドキュメントの取得時に索引は使用されません。これは、NULL
がフィルタ述語として使用されている場合(たとえば、json_value
... IS NULL
)、またはORDER BY
句でjson_value
が使用されている場合、デフォルトで関数ベースの索引が使用されないことを意味します。
NULL
値を索引付けしてORDER BY
句でjson_value
を使用できるようにするには、索引作成文に定数値(任意の値)を追加する必要があります。
CREATE INDEX po_num_idx1 ON j_purchaseorder po
(po.po_document.PONumber.number(), 42);
ただし、索引のサイズは大きくなります。
例29-3 JSONフィールドに対するファンクション・ベースの索引の作成: JSON_VALUE
項目メソッドnumber()
を使用すると、数値型の索引が作成されます。かわりに、RETURNING NUMBER
句を使用することもできます。
CREATE UNIQUE INDEX po_num_idx2 ON j_purchaseorder
(json_value(po_document, '$.PONumber.number()'
ERROR ON ERROR));
例29-4 JSON_VALUEファンクション・ベースの索引に対するNULL ON EMPTYの指定
RETURNING VARCHAR2(200)
句を指定すると、索引は最大長200文字のSQL文字列になります。かわりにパス式に項目メソッドstring()
を使用できますが、その場合は、デフォルトの戻り型VARCHAR2(4000)
が使用されます。
句NULL ON EMPTY
が指定されているため、索引po_ref_idx1
は、Reference
フィールドがないJSON文書にも索引を付けることができます。
CREATE UNIQUE INDEX po_ref_idx1 ON j_purchaseorder
(json_value(po_document, '$.Reference'
RETURNING VARCHAR2(200) ERROR ON ERROR
NULL ON EMPTY));
29.5 JSON_VALUE関数ベースの索引とJSON_TABLE問合せの使用
ERROR ON ERROR
を指定したjson_value
を使用して作成された索引は、json_table
を含む問合せに使用できます。この場合、この索引が索引付きパスに対する制約として機能することで、JSONデータ内の項目ごとに必ず1つの(非null
)スカラーJSON値のみが投影されるようになります。
この方法で索引を使用する場合は、次の各条件が満たされている必要があります。
-
問合せの
WHERE
句は、json_table
によって投影される列を参照する。 -
その列のデータ型は、索引定義に使用されたデータ型と一致する。
-
その列を対象とする有効なSQL/JSONパスが、索引付けされたパス式と一致する。
そのため、例29-5の問合せでは、例29-3で作成した索引を使用しています。
ノート:
json_value
式またはドット表記法を使用して作成された関数ベースの索引を、問合せでWHERE
句が出現するたびに取得できるのは、この発生がSQLの比較条件(>=
など)で使用されている場合のみです。特に、これは、条件IS NULL
またはIS NOT NULL
で使用される存在としては選択されません。
SQL比較条件の詳細は、Oracle Database SQL言語リファレンスを参照してください。
例29-5 JSON_VALUEファンクション・ベースの索引とJSON_TABLE問合せの使用
列のSQL型NUMBER(5)
が索引に使用されている型と一致するため、索引を選択できます。
SELECT jt.*
FROM j_purchaseorder po,
json_table(po.po_document, '$'
COLUMNS po_number NUMBER(5) PATH '$.PONumber',
reference VARCHAR2(30 CHAR) PATH '$.Reference',
requestor VARCHAR2(32 CHAR) PATH '$.Requestor',
userid VARCHAR2(10 CHAR) PATH '$.User',
costcenter VARCHAR2(16 CHAR) PATH '$.CostCenter') jt
WHERE po_number = 1600;
親トピック: JSONデータの索引
29.6 JSON_VALUE関数ベースの索引とJSON_EXISTS問合せの使用
ERROR ON ERROR
を指定したSQL/JSONファンクションjson_value
を使用して作成された索引は、SQL/JSON条件json_exists
を含む問合せに使用できます。
問合せの比較のいずれかでjson_value
関数ベースの索引が選択されるためには、その比較の型が、索引のSQL戻りデータ型と同じである必要があります。使用されるSQLデータ型は、double()
、float()
、number()
、string()
、timestamp()
、date()
、dateWithTime()
、dsInterval()
およびymInterval()
の項目メソッドで言及されたデータ型です(SQL/JSONパス式の項目メソッドを参照)。
たとえば、索引で数値が戻される場合は、比較の型も数値である必要があります。問合せのフィルタ式に、json_value
索引と一致する複数の比較が含まれる場合は、これらの索引の1つがオプティマイザによって選択されます。
比較の型は、次のように決まります。
-
2つの比較語(比較の両側)のSQLデータ型が異なる場合、比較の型は不明になり、索引は選択されません。両方の型が同じ場合は、その型が比較の型になります。
-
1つの比較語のSQLデータ型が文字列(テキスト・リテラル)である場合、比較の型は、もう一方の比較語の型になります。
-
1つの比較語が、項目メソッドによってSQLの一致型が強制される関数ステップを持つパス式の場合、その型も比較語の型になります。SQLの一致型が強制される項目メソッドは、
double()
、float()
、number()
、string()
、timestamp()
、date()
、dateWithTime()
,dsInterval()
およびymInterval()
です。 -
1つの比較語が、前述のような関数ステップを持たないパス式の場合、その型はSQL文字列(テキスト・リテラル)になります。
例29-3では、フィールドPONumber
のjson_value
に対するファンクション・ベースの索引を作成します。この索引は、NUMBER
値に索引付けします。
例29-6、例29-7および例29-8のそれぞれの問合せでは、json_exists
条件を評価するときに、この索引を使用できます。これらの各問合せで、絶対パス式$.PONumber
に相対的な単純なパス式に関連する比較が使用されます。それぞれの場合の単純な相対パス式は、カレント・フィルタ項目(@
)を対象としますが、例29-8の場合は、照合データをSQLデータ型NUMBER
に変換(キャスト)します。
例29-6 リテラル数値と比較したフィールドを対象としたJSON_EXISTS問合せ
この問合せでは、次の理由から索引が利用されます。
-
1つの比較語が、関数ステップを持たないパス式であるため、その型はSQL文字列(テキスト・リテラル)である。
-
1つの比較語が文字列型であるため、比較の型はもう一方の語の型になり、その型が数値である(もう一方の語は数字)。
-
(唯一の)比較の型が、索引で戻される型(数値)と同じである。
SELECT count(*) FROM j_purchaseorder
WHERE json_exists(po_document, '$.PONumber?(@ > 1500)');
例29-7 変数値と比較したフィールドを対象としたJSON_EXISTS問合せ
この問合せでは、次の理由から索引を利用できます。
-
1つの比較語が、関数ステップを持たないパス式であるため、その型はSQL文字列(テキスト・リテラル)である。
-
1つの比較語が文字列型であるため、比較はもう一方の語の型を持つことになり、この型が数値である(もう一方の語は、数値にバインドされた変数)。
-
(唯一の)比較の型が、索引で戻される型(数値)と同じである。
SELECT count(*) FROM j_purchaseorder
WHERE json_exists(po_document, '$.PONumber?(@ > $d)'
PASSING 1500 AS "d");
例29-8 変数値と比較して数値にキャストされるフィールドを対象としたJSON_EXISTS問合せ
この問合せでは、次の理由から索引を利用できます。
-
1つの比較語が、項目メソッド(
number()
)によって照合データが数値に変換される関数ステップを持つパス式であるため、比較語の型はSQL数値である。 -
もう一方の比較語が、SQL型数値を持つ数値である。比較語の型が一致しているため、比較も同じ型である数値になる。
-
(唯一の)比較の型が、索引で戻される型(数値)と同じである。
SELECT count(*) FROM j_purchaseorder
WHERE json_exists(po_document, '$.PONumber?(@.number() > $d)'
PASSING 1500 AS "d");
例29-9 フィールド比較の論理積を対象としたJSON_EXISTS問合せ
例29-6と同様に、この問合せではフィールドPONumber
の索引を利用できます。json_value
索引がフィールドReference
に対しても定義されている場合、オプティマイザがこの問合せに対してどちらの索引を使用するかを選択します。
SELECT count(*) FROM j_purchaseorder
WHERE json_exists(po_document,
'$?(@.PONumber > 1500
&& @.Reference == "ABULL-20140421")');
29.7 JSON_VALUEの索引付けおよび問合せに関するデータ型の考慮事項
SQL/JSONファンクションjson_value
を使用して作成されたファンクション・ベースの索引では、問合せでjson_value
によって返されたデータ型が索引で指定された型と一致する必要があります。
RETURNING DATE
がjson_value
とともに使用される場合、索引が選択されるためには、同じ時間処理動作(切捨てまたは保存)を索引と問合せの両方で使用する必要があります。つまり、両方でRETURNING DATE PRESERVE TIME
を使用するか、両方でRETURNING DATE TRUNCATE TIME
(または、切捨てがデフォルトの動作であるためRETURNING DATE
)を使用する必要があります。
デフォルトでは、SQL/JSONファンクションjson_value
はVARCHAR2
値を戻します。json_value
を使用して関数ベースの索引を作成する場合、RETURNING
句または項目メソッドを使用して異なる戻りデータ型を指定しないかぎり、非VARCHAR2
値を想定する問合せに対してこの索引は選択されません。
たとえば、例29-10の問合せの場合は、json_value
でRETURNING NUMBER
が使用されています。この問合せに対して例29-3で作成した索引を選択できますが、これは、索引付けされたjson_value
式で戻り型としてNUMBER
が指定されているためです。索引にキーワードRETURNING NUMBER
が指定されていない場合、指定される戻り型はVARCHAR2(4000)
(デフォルト)になり、そのような問合せに対して索引は選択されません。
同様に、例29-2で作成した索引は、戻り型NUMBER
が強制される項目メソッドnumber()
を使用しているため、この問合せに対して選択できます。
ここで、戻される値の型がVARCHAR2
になるようにRETURNING
句なしでjson_value
を使用している例29-11と例29-12の問合せについて検討してみます。
例29-11では、SQLファンクションto_number
により、json_value
によって戻されるVARCHAR2
値を明示的に数値に変換しています。同様に、例29-12では、比較条件> (より大きい)により、暗黙的に値を数値に変換しています。
例29-3と例29-2の索引のいずれも、これらの問合せに対して選択されません。これらの問合せでは、いずれの事例でも型キャストのために正しい結果が戻される可能性がありますが、これらの索引を使用してこれらの問合せを評価することはできません。
また、一部のデータを特定のデータ型に変換できない場合に何が起きるかについても検討してください。たとえば、例29-10、例29-11および例29-12の問合せが実行された場合、"alpha"
などのPONumber
値はどうなるでしょうか。
例29-11および例29-12の場合、値を数値にキャストしようとするために問合せはエラーで停止します。ただし、例29-10の場合、デフォルトのエラー処理動作がNULL ON ERROR
であるため、非数値の"alpha"
は単純にフィルタで除外されます。値には索引が付けられますが、問合せに対しては無視されます。
同様に、たとえばDEFAULT '1000' ON ERROR
が問合せで使用されたとすると、つまり、数値のデフォルト値が指定されたとすると、値"alpha"
に対してエラーは発生しません。この場合、デフォルト値の1000
が使用されます。
ノート:
指定された問合せに対してSQL/JSONファンクションjson_value
に基づくファンクション・ベースの索引が選択されるためには、索引と問合せの両方で同じ戻りデータ型および処理方法(エラー、空、不一致)を使用する必要があります。
つまり、問合せの戻り型または処理方法を変更して、索引に指定されたものと一致しないようにする場合は、問合せパターンに依存する永続オブジェクトを再構築する必要があります。(同じことが、マテリアライズド・ビュー、パーティション、チェック制約およびそのパターンに依存するPL/SQLサブプログラムにも当てはまります。)
例29-10 JSON_VALUE問合せと明示的なRETURNING NUMBER
SELECT count(*) FROM j_purchaseorder po
WHERE json_value(po_document, '$.PONumber' RETURNING NUMBER) > 1500;
例29-11 JSON_VALUE問合せと明示的な数値変換
SELECT count(*) FROM j_purchaseorder po
WHERE to_number(json_value(po_document, '$.PONumber')) > 1500;
例29-12 JSON_VALUE問合せと暗黙的な数値変換
SELECT count(*) FROM j_purchaseorder po
WHERE json_value(po_document, '$.PONumber') > 1500;
親トピック: JSONデータの索引
29.8 JSON_EXISTSの複数値関数ベースの索引の作成
JSON
データ型として格納されるJSONデータの場合、SQL/JSON条件json_exists
に複数値関数ベースの索引を使用できます。このような索引は、スカラーJSON値を個別に、またはJSON配列内で対象とします。
複数値索引の主な用途は、配列内のスカラー値の索引付けです。これにはスカラー配列要素が含まれますが、オブジェクト配列要素のスカラー・フィールド値も含まれます。
複数値索引では単一のスカラー値に索引付けすることもできますが、単一の値を対象とする問合せでは、一般に、Bツリー索引またはビットマップ索引を使用する方がパフォーマンスが高くなります。
問合せでは、SELECT
文のWHERE
句でjson_exists
を使用します。条件json_exists
は、対象となるデータが問合せのSQL/JSONパス式(または同等の単純なドット表記法構文)と一致する場合にtrueを返します。それ以外の場合、falseを返します。通常は、パス式にフィルタ式を含めます。つまり、照合では対象データがフィルタを満たす必要があります。
複数値索引を作成するには、キーワードMULTIVALUE
を指定したCREATE INDEX
を使用し、SQL/JSONファンクションjson_table
の構文または索引付きデータへのパスを指定する問合せで使用するのと同じ単純なドット表記法構文を使用します。(ただし、SQL NESTED
句をjson_table
のかわりに使用することはできません。使用するとコンパイル時にエラーが発生します。)
コンポジット関数ベースの索引を作成して、複数の仮想列(複数のJSONフィールド)に索引付けできます。コンポジット索引は、関数ベースの索引のセットのように動作します。問合せに使用する場合、ファンクションjson_table
を使用して、指定したJSONフィールド値をSQLスカラー値の仮想列として投影します。同様に、索引の定義に使用する場合、json_table
で指定したフィールド値はコンポジット関数ベースの索引として索引付けされます。
json_table
構文を使用して複数値索引を作成する場合は、エラー処理句ERROR ON ERROR NULL ON EMPTY NULL ON MISMATCH
を使用する必要があります。そうしないと、問合せのコンパイル時エラーが発生します。json_table
を使用せずに単純なドット表記法構文を使用する場合、これらの句の動作は暗黙的に指定されます。
json_table
構文を使用する場合、FOR ORDINALITY
句を使用すると、特定の配列位置を対象とする問合せに対して索引を使用できるようになります。(SQL/JSONファンクションJSON_TABLEのCOLUMNS句を参照。)
複数値索引を問合せによって選択するには、索引付けするデータのSQL型を索引で指定する必要があり、問合せ結果のSQL型は索引で指定された型と一致する必要があります。
非コンポジット複数地索引を作成する場合、つまりjson_table
構文を使用しない場合、索引指定には(binary()
およびdateWithTime()
以外の)データ型変換項目メソッドを含めてSQLデータ型を示す必要があります。データ型変換項目メソッドの詳細は、SQL/JSONパス式の項目メソッドを参照してください。
索引の名前に"only"を含む項目メソッドが使用されている場合は、その同じ項目メソッドを使用している問合せのみが索引を選択できます。それ以外の("only"が含まれていないメソッドを使用しているか、メソッドを使用していない)場合、項目メソッドで指定された型に変換できるスカラー値を(おそらく配列要素として)対象とする問合せで索引を選択できます。
たとえば、項目メソッドnumberOnly()
を使用する複数値索引は、numberOnly()
も使用する問合せに対してのみ選択できます。ただし、number()
を使用する索引または項目メソッドを使用していない索引は、数値に変換可能な任意のスカラー(文字列"3.14"
など)に一致する問合せに対して選択できます。
コンポジット複数値索引を作成する場合、json_table
の仮想列型では使用するSQL型を指定します。つまり、指定したSQL型に変換できるデータの問合せでは、索引を選択できます。
ただし、非コンポジット索引の場合と同様に、名前に"only"を含むデータ型変換項目メソッドを使用して、指定された列型をオーバーライド(さらに制約)できます。項目メソッドは列パス式で使用します。
たとえば、列型がNUMBER
と指定されている場合、数値に変換可能なデータ(文字列"3.14"
など)に一致する問合せでは、索引を選択できます。ただし、列パス式で項目メソッドnumberOnly()
を使用している場合は、同様にnumberOnly()
を使用している問合せのみが索引を選択できます。
特定のターゲットに複数の複数値索引を作成できます。たとえば、フィールドmonth
に対して項目メソッドnumber()
を使用する索引を作成し、同じフィールドに対して項目メソッドstring()
を使用する別の索引を作成できます。
複数値索引を作成する方法として、次のことはできません。
-
コンポジット複数値索引の作成に使用される
json_table
式で、兄弟のネストされた配列を指定できません。しようとすると、エラーが発生します。複数の配列に索引付けできますが、兄弟にすることはできません。つまり、同じ親フィールドを持つことはできません。 -
SQL
NESTED
句の使用(JSON_TABLEの代替のSQL NESTED句を参照)。
json_table
仮想列のスカラーJSON値の型とそれに対応するスカラーSQLデータ型の間の型エラー不一致は、表17-2で示されている型の非互換性、またはSQLデータ型の制約が厳しすぎること(データを格納するには小さすぎる)が原因である可能性があります。
エラー処理ERROR ON ERROR NULL ON EMPTY NULL ON MISMATCH
では、1つ目の種類の不一致に対してSQL NULL
を返しますが、2つ目の種類に対してエラーが発生します。たとえば、JSON文字列データに対してSQL型NUMBER
の索引を作成する際に型の非互換性が許容されますが、JSON文字列値が"hello"
のデータに対してSQL型VARCHAR(2)
を使用して索引を作成しようとするとエラーが発生します。これは、データが2文字を超えるためです。
例29-13 表PARTS_TAB (複数値索引の例)
JSON
データ型列jparts
が含まれる表parts_tab
が、この複数値索引の例で使用されています。JSONデータには、値がスカラー要素を含む配列であるフィールドsubparts
が含まれます。
CREATE TABLE parts_tab (id NUMBER, jparts JSON);
INSERT INTO parts_tab VALUES
(1, '{"parts" : [{"partno" : 3, "subparts" : [510, 580, 520]},
{"partno" : 4, "subparts" : 730}]}');
INSERT INTO parts_tab VALUES
(2, '{"parts" : [{"partno" : 7, "subparts" : [410, 420, 410]},
{"partno" : 4, "subparts" : [710, 730, 730]}]}');
例29-14 JSON_EXISTSの複数値索引の作成
ここで作成された複数値索引は、フィールドsubparts
の値を索引付けします。単純なドット表記法構文を使用する際は、表の別名(この場合はt
)が必要です。
問合せの対象となるsubparts
値が配列の場合、数値である配列要素の索引を選択できます。値がスカラーの場合、スカラーが数値であれば索引を選択できます。
表parts_tab
のデータを考えた場合、最初の行(id
1
)の配列parts
の各オブジェクトのsubparts
フィールドが索引付けされます。1つ目のオブジェクトのフィールドは、配列値に数値(510
、580
および520
)の要素があるため、2つ目のオブジェクトのフィールドは、値が数値(730
)であるためです。
項目メソッドnumber()
がnumberOnly()
のかわりに索引定義で使用された場合、数値に変換可能な数値以外のスカラー値(文字列"730"
など)も索引付けされます。
CREATE MULTIVALUE INDEX mvi ON parts_tab t
(t.jparts.parts.subparts.numberOnly());
例29-15 JSON_EXISTSのコンポジット複数値索引の作成
この例では、フィールドpartno
とフィールドsubparts
の両方を対象とするコンポジット複数値索引を作成する2つの同等の方法を示します。コンポジット索引は、これらの2つのフィールドを対象とする2つの関数ベースの索引のセットのように動作します。
1つ目の問合せでは、json_table
構文をSQL/JSONパス式とともに行パターン$.parts[*]
に使用します。2つ目では、単純なドット表記法を行パターンに使用します。その他の点では、コードはどちらも同じです。json_table
を使用した複数値索引の作成では常にそうであるように、エラー処理はERROR ON ERROR NULL ON EMPTY NULL ON MISMATCH
と指定します。
ここでは、列PARTNUM
にSQLデータ型NUMBER(10)
が指定されています。つまり、フィールドpartno
を対象とする問合せに索引を使用するには、そのフィールドの値がそのデータ型に変換できる必要があります。
-
表17-2で示されているように、型が一般的に互換性がないために型変換できない場合は、
NULL ON MISMATCH
エラー・ハンドラによってSQLNULL
が返されます。この例として、型NUMBER(10)
のSQLpartNum
列に対するpartno
文字列値"hello"
があります。 -
一方、SQLデータ型の記憶域の制約が厳しすぎる場合は、エラーが発生します。つまり、索引は作成されません。この例として、
"1234567890123"
など、10文字を超えるpartno
文字列があります。
CREATE MULTIVALUE INDEX cmvi_1 ON parts_tab
(json_table(jparts, '$.parts[*]'
ERROR ON ERROR NULL ON EMPTY NULL ON MISMATCH
COLUMNS (partNum NUMBER(10) PATH '$.partno',
NESTED
PATH '$.subparts[*]'
COLUMNS (subpartNum NUMBER(20) PATH '$'))));
CREATE MULTIVALUE INDEX cmvi_1 ON parts_tab t
(t.jparts.parts[*]
ERROR ON ERROR NULL ON EMPTY NULL ON MISMATCH
COLUMNS (partNum NUMBER(10) PATH '$.partno',
NESTED subparts[*]
COLUMNS (subpartNum NUMBER(20) PATH '$'))));
例29-16 配列位置を対象にできるコンポジット複数値索引の作成
この例のコードは例29-15に似ていますが、このコードでは、さらに順序性のための仮想列SEQ
を指定しています。つまり、その直前の列(SUBPARTNUM
)の値には、配列subparts
内の(1ベースの)位置を使用してアクセスできます。(FOR ORDINALITY
列のSQLデータ型は、常にNUMBER
です。)
常に、COLUMNS
句内の多くても1つのエントリでは、列名の後に、生成された行番号の列(SQLデータ型NUMBER
)を指定する、1から始まるFOR ORDINALITY
を続けることができます。そうしないと、索引の作成時にエラーが発生します。
json_table
構文のこの一般ルールに加えて、
-
json_table
を使用して複数値索引を作成する場合、FOR ORDINALITY
列はjson_tableの最後の列である必要があります。(json_table
が問合せで使用される場合、これは必須ではありません。索引の作成にのみ適用されます。) -
json_table
を使用して作成した複数値索引を特定の問合せに対して選択するには、その問合せではjson_table
式の最初の仮想列に対応するJSONフィールドにフィルタ式を適用する必要があります。
位置で配列要素を対象とする問合せで配列位置に対して複数値索引を選択するには、それらの配列要素の索引列がFOR ORDINALITY
列の直前の列である必要があります
(ここでのコードでは、単純なドット表記法を行パターンに使用します。かわりにSQL/JSONパス式を行パターンに使用した場合、残りのコードは同じになります。)
CREATE MULTIVALUE INDEX cmvi_2 ON parts_tab t
(t.jparts.parts[*]
ERROR ON ERROR NULL ON EMPTY NULL ON MISMATCH
COLUMNS (partNum NUMBER(10) PATH '$.partno',
NESTED subparts[*]
COLUMNS (subpartNum NUMBER(20) PATH '$',
seq FOR ORDINALITY))));
29.9 複数値関数ベースの索引の使用
WHERE
句のjson_exists
問合せでは、対象とするデータが索引に指定されたスカラー型と一致する場合(かつその場合にかぎり)、複数値関数ベースの索引を選択できます。
SQL/JSON条件json_exists
の複数値関数ベースの索引は、個別にまたはJSON配列の要素としてスカラーJSON値を対象とします。複数値索引は、JSON
データ型として格納されているJSONデータに対してのみ定義できます。
条件json_exists
は、対象となるデータが問合せのSQL/JSONパス式(または同等の単純なドット表記法構文)と一致する場合にtrueを返します。それ以外の場合、falseを返します。通常は、パス式にフィルタ式を含めます。つまり、照合では対象データがフィルタを満たす必要があります。
名前に"only"を含むデータ型変換項目メソッド(numberOnly()
など)を使用して定義される複数値索引は、同様に同じ項目メソッドを使用するjson_exists
問合せによってのみ選択できます。つまり、問合せでは同じ項目メソッドを明示的に使用する必要があります。詳細は、JSON_EXISTSの複数値関数ベースの索引の作成を参照してください。
項目メソッドを使用せずに、または名前に"only"が含まれていないデータ型変換項目メソッド(number()
など)を使用して定義された複数値索引は、項目メソッドで指定された型に変換できるスカラー値を(おそらく配列要素として)対象とする問合せで選択できます。データ型変換項目メソッドの詳細は、SQL/JSONパス式の項目メソッドを参照してください。
この例では、WHERE
句でSQL/JSON条件json_exists
を使用して、730
に一致するsubparts
フィールド値がないかチェックします。これらは、複数値索引mvi
、cmvi_1
およびcmvi_2
(JSON_EXISTSの複数値関数ベースの索引の作成で定義)を選択できるどうかの観点で説明されています。JSONスカラー値のSQLスカラー値への変換は、表17-2を参照してください。
例29-17 項目メソッドnumberOnly()を使用したJSON_EXISTS問合せ
この例では、WHERE
句で項目メソッドnumberOnly()
を使用します。パス式が数値のsubparts
値730
(たとえばsubparts : 730
)または1つ以上の数値の要素730
が含まれる配列のsubparts
値(たとえばsubparts:[630, 730, 690, 730]
)を対象とする場合、問合せでは索引mvi
を選択できます。対象となる文字列値"730"
(たとえばsubparts:"730"
またはsubparts:["630", "730", 690, "730"]
)に対しては、索引mvi
を選択できません。
かわりに、項目メソッドnumber()
を使用して索引mvi
が定義されている場合、この問合せでは、数値のsubparts
値730
、文字列のsubparts
値"730"
または数値要素730
または文字列要素"730"
が含まれる配列のsubparts
値に対して索引を選択できます。
SELECT count(*) FROM parts_tab
WHERE json_exists(jparts, '$.parts.subparts?(@.numberOnly() == 730)');
例29-18 項目メソッドnumberOnly()を使用しないJSON_EXISTS問合せ
これらの2つの問合せでは、項目メソッドnumberOnly()
を使用しません。1つ目はメソッドnumber()
を使用します。このメソッドは対象データを数値に変換します(可能な場合)。2つ目は対象データを型変換しません。
索引mvi
は、対象データが数値730
であっても、どちらの問合せでも選択できません。索引はnumberOnly()
を使用して定義されているため、索引を選択するには、問合せでnumberOnly()
を使用する必要があります。
SELECT count(*) FROM parts_tab t
WHERE json_exists(jparts, '$.parts.subparts?(@.number() == 730)');
SELECT count(*) FROM parts_tab t
WHERE json_exists(jparts, '$.parts.subparts?(@ == 730)');
例29-19 複数フィールドをチェックするJSON_EXISTS問合せ
この問合せのフィルタ式では、SQL NUMBER
値4
と一致するpartno
フィールド(おそらくJSON文字列からの変換によるもの)および数値730
と一致するフィールドsubparts
の有無を指定します。
この問合せでは、索引cmvi_1
またはcmvi_2
のいずれかを選択できます。各行に数値4
と一致するparts.partno
値と、数値730
と一致するparts.subparts
値があるため、データのどちらの行もこれらの索引と一致します。subparts
一致の場合、1行目にはsubparts
値730
が、2行目には値730
が含まれる配列であるsubparts
値があります。
SELECT a FROM parts_tab
WHERE json_exists(jparts,'$.parts[*]?(@.partno == 4 &&
@.subparts == 730)');
例29-20 配列要素の位置をチェックするJSON_EXISTS問合せ
この例は例29-19に似ていますが、フィールドpartno
が数値4
と一致する必要があることに加えて、ここでのフィルタ式では、フィールドsubparts
の値が少なくとも2つの要素の配列と一致し、配列の2つ目の要素が数値730
と一致している必要があります。
この問合せでは、位置述語[1]
に対しても含め、索引cmvi_2
を選択できます。索引cmvi_2
では、仮想列subpartNum
を指定します。これは、最終列FOR ORDINALITY
の直前、最後から2番目の列としてJSONフィールドsubparts
に対応します。
この問合せでは索引cmvi_1
も選択できますが、その索引にFOR ORDINALITY
列がないため、使用するには、配列の位置条件[1]
を評価するために追加ステップが必要になります。索引cmvi_2
の使用には、このような追加ステップは必要ないため、このような問合せのパフォーマンスは向上します。
SELECT a FROM parts_tab
WHERE json_exists(jparts,'$.parts[*]?(@.partno == 4 &&
@.subparts[1] == 730)');
29.10 コンポジットBツリー索引を使用した複数のJSONフィールドの索引付け
JSONオブジェクトの複数のフィールドに索引を付けるには、SQL/JSONファンクションjson_value
またはドット表記法構文で複数のパス式を使用してコンポジットBツリー索引を作成できます。
例29-21に、これを示します。対応するJSONデータ(オブジェクト・フィールド)を参照するSQL問合せでは、コンポジット索引が選択されます。例29-22に、これを示します。
または、索引付けするJSONオブジェクトのフィールドの仮想列を作成し、それらの仮想列にコンポジットBツリー索引を作成できます。その場合、仮想列または対応するJSONデータ(オブジェクト・フィールド)を参照するSQL問合せでは、コンポジット索引が選択されます。問合せパフォーマンスはどちらの場合も同じです。
問合せのパフォーマンスを向上させるために実装された索引に、データが論理的に依存することはありません。この実装からの独立をコードに反映するには、(仮想列ではなく)データを直接問い合せます。このようにすることにより、問合せは索引の有無とは関係なく同様に機能します。この場合、索引はパフォーマンスの向上に特化して機能します。
例29-21 JSONオブジェクト・フィールドのコンポジットBツリー索引の作成
CREATE INDEX user_cost_ctr_idx ON
j_purchaseorder(json_value(po_document, '$.User'
RETURNING VARCHAR2(20),
json_value(po_document, '$.CostCenter'
RETURNING VARCHAR2(6)));
例29-22 コンポジットBツリー索引を使用して索引付けされたJSONデータの問合せ
SELECT po_document FROM j_purchaseorder
WHERE json_value(po_document, '$.User') = 'ABULL'
AND json_value(po_document, '$.CostCenter') = 'A50';
29.11 非定型の問合せおよび全文検索のためのJSON検索索引
JSON検索索引は、一般的な索引です。この検索索引によって、(1)非定型の構造的問合せ、つまり、定期的には予測または使用されない可能性のある問合せと、(2)全文検索の両方のパフォーマンスを向上させることができます。これは、JSONデータでの使用に特化して設計されたOracle Text索引です。
JSONデータの全文問合せについては、全文検索問合せを参照してください。このトピックでは、全文検索に必要であり、アドホック問合せにも役立つJSON検索索引の作成および保守について説明します。JSON検索索引でサポートされているアドホック問合せの例を次に示します。
全文検索が含まれる問合せについて、JSON検索索引を作成します。特に想定されていない、つまり定期的に使用されない問合せ(アドホック問合せ)についてもJSON検索索引を作成します。ただし、前もって問合せパターンがわかっている問合せを索引付けするには、通常、そのような特定のパターンを対象とする関数ベースの索引を使用することをお薦めします。関数ベースの索引とJSON検索索引の両方が特定の問合せに適用可能な場合、使用されるのは関数ベースの索引です。
JSON
型として格納されているJSONデータの場合、JSON検索索引を作成して保守する代替方法として、インメモリー列ストア(IM列ストア)にJSON列を移入する方法があります(インメモリーJSONデータを参照)。
ノート:
Oracle Database 12cリリース1 (12.1.0.2)を使用してJSON検索索引を作成した場合は、その索引を削除し、以降のリリースで使用するために、ここで説明するCREATE SEARCH INDEX
を使用して検索索引を新しく作成することをお薦めします。
ノート:
名前が64バイトを超えるオブジェクト・フィールドが含まれるJSONデータを索引付けする場合、Oracle Database 18cより前に作成された任意のJSON検索索引およびOracle Text索引を再構築する必要があります。そのようにしないと、そのようなフィールドは再索引付けされるまで検索可能にならない可能性があります。詳細は、『Oracle Databaseアップグレード・ガイド』を参照してください。
CREATE SEARCH INDEX
にキーワードFOR JSON
を指定して、JSON検索索引を作成します。例29-23および例29-24に、これを示します。
JSON検索索引を作成する列は、JSON
、VARCHAR2
、CLOB
またはBLOB
データ型の列です。整形式のJSONデータのみが含まれていることが認識されている必要があります(つまり、JSON
型であるか、is json
チェック制約がある)。列にJSONデータが含まれていることを認識できない場合、CREATE SEARCH INDEX
でエラーが発生します。
問合せの実行計画内にJSON検索索引の名前が存在する場合は、索引が実際に問合せに対して選択されていることがわかります。例29-25に示すものと似た行が表示されます。
特定の構成可能なオプションのデフォルト設定をオーバーライドする場合は、検索索引の作成時にPARAMETERS
句を指定できます。デフォルト(PARAMETERS
句なし)では、索引は自動的にメンテナンスされ(バックグラウンドで同期され)、テキストと数値の両方の範囲が索引付けされます。
JSON検索索引を使用する問合せにフルテキスト検索または文字列等価検索のみが含まれ、文字列範囲検索、数値検索および時間検索(等価または範囲)が含まれない場合は、パラメータSEARCH_ON
にTEXT
を指定することで、索引のメンテナンス時間とディスク領域をいくらか節約できます。SEARCH_ON
のデフォルト値はTEXT_VALUE
です。これは、テキストと同様に索引の数値の範囲も意味します。
デフォルトでは、JSON検索索引は非同期的にメンテナンスされます。これにより、同期によってDML操作にもたらされる可能性がある悪影響が少なくなります。(索引は、同期されるまでデータの追加または変更が反映されません。ただし、同期されていない場合でも、削除はただちに反映されます。)
デフォルトでは、JSON検索索引はバックグラウンドで自動的に同期されます。この動作は、様々なユースケースで索引の同期設定を変更することでオーバーライドできます。
-
コミット時に同期。
これは、コミットが頻繁に行われず、コミットされた変更を他の操作(問合せなど)がすぐに参照できることが重要である場合に適しています。(索引が失効していると、コミットされていない変更を参照できないことがあります。)例29-24では、コミット時に同期される検索索引を作成します。
-
オンデマンドで同期(たとえば、データベースの負荷が減少したとき)。
通常、これは頻繁に実行しません。コミット時の同期または間隔での同期よりも少ない頻度で、索引が同期されます。DMLのパフォーマンスが特に重要な場合は、この方法が一般的に適しています。
パッケージCTX_DDL
のCTX_DDL.sync_index
などのプロシージャを起動して索引を手動で同期化する必要がある場合は、権限CTXAPP
が必要です。
静的ディクショナリ・ビューCTX_USER_INDEXES
には、JSON検索索引を含め、既存のOracle Text索引に関する情報が含まれています。たとえば、次の問合せでは、すべてのOracle Text索引の同期タイプおよびメンテナンス・タイプがリストされます。
SELECT IDX_NAME, IDX_SYNC_TYPE, IDX_MAINTENANCE_TYPE FROM CTX_USER_INDEXES;
ノート:
JSON検索索引j_s_idx
を変更するには、ALTER INDEX j_s_idx REBUILD ...
(ALTER SEARCH INDEX j_s_idx ...
ではない)を使用します。
例29-23 デフォルトの動作によるJSON検索索引の作成
この例では、デフォルトの動作をするJSON検索索引を作成します。索引は自動的にメンテナンスされ(バックグラウンドで同期され)、テキストと数値の両方の範囲が索引付けされます。
CREATE SEARCH INDEX po_search_idx ON j_purchaseorder (po_document)
FOR JSON;
このコードは、同等のものです。PARAMETERS句を使用して、自動メンテナンスを明示的に指定します。
CREATE SEARCH INDEX po_search_idx ON j_purchaseorder (po_document)
FOR JSON PARAMETERS ('MAINTENANCE AUTO');
例29-24 コミット時に同期されるJSON検索索引の作成
この例では、PARAMETERS
句を使用して、COMMIT
時に新しいデータを同期する索引を作成します。
CREATE SEARCH INDEX po_search_idx ON j_purchaseorder (po_document)
FOR JSON PARAMETERS ('SYNC (ON COMMIT)');
例29-25 JSON検索索引が使用されていることを示す実行計画
|* 2| DOMAIN INDEX | PO_SEARCH_IDX | | | 4 (0)
例29-26 いくつかの非定型JSON問合せ
この問合せでは、船積み依頼書の住所に国が含まれる文書を選択します。
SELECT po_document FROM j_purchaseorder
WHERE json_exists(po_document,
'$.ShippingInstructions.Address.country');
この問合せでは、ユーザーAKHOO
が含まれる文書で、注文された項目が8個より多いものを選択します。ここでは、数値範囲の索引付けが利用されます。
SELECT po_document FROM j_purchaseorder
WHERE json_exists(po_document, '$?(@.User == "AKHOO"
&& @.LineItems.Quantity > 8)');
この問合せでは、ユーザーがAKHOO
の文書が選択されます。ここでは、WHERE
句で、json_exists
のかわりにjson_value
が使用されます。
SELECT po_document FROM j_purchaseorder
WHERE json_value(po_document, '$.User') = 'AKHOO';
関連項目
関連項目:
-
CREATE SEARCH INDEX
のPARAMETERS
句の詳細は、Oracle Textリファレンスを参照してください。 -
ALTER INDEX
...REBUILD
のPARAMETERS
句の詳細は、Oracle Textリファレンスを参照してください。 -
『Oracle Textアプリケーション開発者ガイド』の索引の自動メンテナンスの使用に関する項
-
JSON検索索引の同期化の詳細は、Oracle TextリファレンスのCREATE INDEXを参照してください。
-
既存のOracle Text索引のプロパティの詳細は、CTX_USER_INDEXESを参照してください
-
JSON検索索引のパフォーマンスの最適化および調整の詳細は、Oracle Textアプリケーション開発者ガイドを参照してください。
親トピック: JSONデータの索引