30.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データ型の間の型エラー不一致は、表18-2で示されている型の非互換性、またはSQLデータ型の制約が厳しすぎること(データを格納するには小さすぎる)が原因である可能性があります。
エラー処理ERROR ON ERROR NULL ON EMPTY NULL ON MISMATCH
では、1つ目の種類の不一致に対してSQL NULL
を返しますが、2つ目の種類に対してエラーが発生します。たとえば、JSON文字列データに対してSQL型NUMBER
の索引を作成する際に型の非互換性が許容されますが、JSON文字列値が"hello"
のデータに対してSQL型VARCHAR(2)
を使用して索引を作成しようとするとエラーが発生します。これは、データが2文字を超えるためです。
例30-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]}]}');
例30-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());
例30-15 JSON_EXISTSのコンポジット複数値索引の作成
この例では、フィールドpartno
とフィールドsubparts
の両方を対象とするコンポジット複数値索引を作成します。コンポジット索引は、これらの2つのフィールドを対象とする2つの関数ベースの索引のセットのように動作します。
この問合せでは、json_table
構文をSQL/JSONパス式とともに行パターン$.parts[*]
に使用します。json_table
を使用した複数値索引の作成では常にそうであるように、エラー処理はERROR ON ERROR NULL ON EMPTY NULL ON MISMATCH
と指定します。
ここでは、列PARTNUM
にSQLデータ型NUMBER(10)
が指定されています。つまり、フィールドpartno
を対象とする問合せに索引を使用するには、そのフィールドの値がそのデータ型に変換できる必要があります。
-
表18-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 '$'))));
例30-16 配列位置を対象にできるコンポジット複数値索引の作成
この例のコードは例30-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 PATH subparts[*]
COLUMNS (subpartNum NUMBER(20) PATH '$',
seq FOR ORDINALITY))));