14 SQL/JSONパス式

Oracle Databaseでは、SQL/JSONパス式を使用したJSONデータへのSQLアクセスが提供されます。

14.1 SQL/JSONパス式の概要

Oracle Databaseでは、SQL/JSONパス式を使用したJSONデータへのSQLアクセスが提供されます。

JSONは、JavaScript値の表記法です。JSONデータがデータベースに格納されている場合、XMLデータ用のXQueryまたはXPath式とある程度似たパス式を使用してこのデータを問い合せることができます。SQL/XMLでXQuery式を使用してXMLデータに対するSQLのアクセスが許可されるのと同じように、Oracle Databaseでは、SQL/JSONパス式を使用してJSONデータに対するSQLのアクセスが提供されます。

SQL/JSONパス式の構文は単純です。パス式では、この式と一致するかこの式を満たす0(ゼロ)個以上のJSON値が選択されます。

SQL/JSON条件json_existsの場合、少なくとも1つの値が一致する場合はtrueが戻され、一致する値がない場合はfalseが戻されます。単一の値が一致した場合、この値がスカラーである場合はSQL/JSONファンクションjson_valueによってこの値が戻され、スカラーでない場合はエラーが発生します。パス式に一致する値がない場合は、json_valueによってSQL NULLが戻されます。

SQL/JSONファンクションjson_queryは、一致する値をすべて戻します。つまり、この関数は複数の値を戻すことができます。この動作は、XQueryの場合のように値のシーケンスを戻す動作とみなすことも、複数の値を戻す動作とみなすこともできます。(ユーザーから見えるシーケンスが現れることはありません。)

どのような場合でも、パス式の照合では、パス式の各ステップを順番に照合しようとします。いずれかのステップの照合が失敗すると、後続のステップの照合は行われず、パス式の照合は失敗します。各ステップの照合が成功すると、パス式の照合は成功します。

14.2 SQL/JSONパス式の構文

SQL/JSONパス式は、パスの部分を選択するためにSQL/JSONファンクションおよび条件によってJSONデータと照合されます。パス式にはワイルドカードおよび配列範囲を使用できます。照合では大文字/小文字が区別されます。

SQL/JSONパス式およびJSONデータをSQL/JSONファンクションまたは条件に渡します。パス式はデータに対して照合され、一致するデータが特定のSQL/JSONファンクションまたは条件によって処理されます。この照合プロセスは、パス式が一致データを関数または条件に戻すという観点で検討することができます。

14.2.1 基本的なSQL/JSONパス式の構文

SQL/JSONパス式の基本的な構文を示します。これは、コンテキスト項目記号($)とその後のゼロ個以上のオブジェクト、配列および子孫ステップで構成されます。それぞれの後にフィルタ式を続けることができ、オプションでファンクション・ステップを指定できます。例を示します。

ただし、この基本的な構文は、非配列パターンに対する配列の照合および配列パターンに対する非配列の照合の緩和によって拡張されます。SQL/JSONパス式の構文の緩和を参照してください。

SQL/JSONパス式に対するデータの照合では、大/小文字が区別されます。

  • SQL/JSONの基本的なパス式(ここでは単にパス式とも呼びます)は、絶対パス式または相対パス式です。

  • 絶対パス式は、パス式のコンテキスト項目(つまり、照合するJSONデータ)を表すドル記号($)から始まります。このデータは、SQL/JSONファンクションに引数として渡されたSQL式の評価結果です。ドル記号の後にゼロ個以上の非ファンクション・ステップが続き、その後にオプションのファンクション・ステップが続きます。

  • 相対パス式は、アット・マーク(@)の後にゼロ個以上の非ファンクション・ステップが続き、その後にオプションのファンクション・ステップが続く形式です。ドル記号($)のかわりにアット・マークを使用する点を除き、絶対パス式と同じ構文になります。

    相対パス式はフィルタ式(フィルタと短縮される)内で使用されます。アット・マークは、パス式のカレント・フィルタ項目(相対パス式を含むフィルタに先行する(周囲の)パス式の部分に一致するJSONデータ)を表します。相対パス式は、絶対パス式がコンテキスト項目に対して照合されるのと同じように、カレント・フィルタ項目に対して照合されます。

  • 非ファンクション・ステップは、オブジェクト・ステップ配列ステップまたは子孫ステップの後にオプションのフィルタ式が続く形式です。

  • 単一のファンクション・ステップは、基本パス式(絶対または相対)ではオプションです。このステップがあったら、これがパス式の最後のステップになります。これはピリオド(.) (ドットと呼ぶこともある)の後にSQL/JSON項目メソッドが続き、その後に左カッコ(()および右カッコ())が続く形式です。これらのカッコの中にホワイトスペースを入れることができます(このようなホワイトスペースは重要ではありません)。

    項目メソッドは、同じパス式内の残りの部分、かつファンクション・ステップに先行する部分が対象とするデータに適用されます。項目メソッドは、目的のデータを変換するために使用されます。引数としてパス式に渡されたSQLファンクションまたは条件は、対象データのかわりとして変換済データを使用します。

  • オブジェクト・ステップは、ピリオド(.)の後にオブジェクト・フィールド名またはすべてのフィールド(の値)を表すアスタリスク (*)ワイルドカードが続く形式です。フィールド名はにすることができます。この場合は、""(ホワイトスペースなし)と記述する必要があります。空でないフィールド名は、大文字または小文字の文字A-Zで始まり、このような文字または10進数字(0-9)のみを含む必要があり、それ以外の場合は、二重引用符(")で囲む必要があります。

    オブジェクト・ステップは、指定されたフィールドのを戻します。フィールドにワイルドカードが使用されている場合、すべてのフィールドの値が特に順序は指定されずに戻されます。

  • 配列ステップは、左大カッコ([)の後ろに、すべての配列要素を表すアスタリスク(*)ワイルドカードまたはカンマ(,)で区切られた1つ以上の特定の配列索引または範囲指定のどちらかが続き、その後ろに右大カッコ(])が付いた形式です。

    アスタリスクと配列索引または範囲指定の両方を使用すると、エラーが発生します。索引または範囲の指定がない場合、エラーが発生します。[]は有効な配列ステップではありません。

    配列索引は、単一の配列の位置(整数(0、1、2...))を指定します。このため、配列索引はリテラルの整数(012、…)であり、配列位置および索引付けは配列に関するJavaScript規則にあるようにゼロベースです。先頭の配列要素は、索引0 (位置0を指定)です。

    任意のサイズの空ではない配列の末尾の要素は、索引lastを使用して参照できます。

    配列索引には、last - Nという形式もあります。ここで、-はマイナス記号(ハイフン)で、Nは配列サイズ-1を超えないリテラルの整数(012、…)です。

    末尾の1つ前の配列要素は、索引last-1を使用して参照できます。末尾の2つ前の配列要素は索引last-2で、以下同様になります。マイナス記号(ハイフン)の前後の空白は無視されます。

    たとえば、配列["a", "b", 42]の場合、索引1 (位置1)の要素は文字列"b" (2番目の配列要素)です。索引2または索引lastの要素は、数値42です。索引0またはlast-2の要素は、"a"です。

    Oracle SQLファンクションjson_transformでは、last + Nという形式の索引も使用できます。ここで、Nは整数です。これにより、現在の配列サイズ-1を超える位置を指定することによって、既存の配列に新しい要素を追加できます。プラス記号の前後の空白は無視されます。この形式の索引は、範囲指定(次を参照)などの他の索引と組み合せて使用できません。その場合は、エラーが発生します。

    範囲指定の形式は、N to Mです。ここで、NMは配列索引であり、toの前後には1つ以上の空白文字を記述します。脚注1

    範囲指定N to Mは、NからMまでのすべての索引(NMを含む)を明示的に昇順で指定することと同じです。

    範囲指定では、NMの順序は重要ではありません。3番目から6番目の要素の範囲は、2 to 5または5 to 2として記述できます。6個の要素を持つ配列の場合は、同じ範囲を2 to lastまたはlast to 2として記述できます。範囲指定N to N (toの両側に同じ索引N)は、単一の索引Nと同じです([N, N]と同じではありません)。

    配列ステップに配列索引および配列範囲を指定する順序は重要であり、パス式を使用するファンクションで生成される配列に反映されます。

    同じ配列ステップ内の複数の範囲指定は、個別に処理されます。特に、重複する範囲では、重複している要素が繰り返されます。

    たとえば、配列ラッパー(複数の問合せ結果をラップして単一のJSON配列を返します)を指定したSQL/JSONファンクションjson_queryに、配列ステップ[3 to 1, 2 to 4, last-1 to last-2, 0, 0]を指定したパス式を渡して、問合せを行うとします。この問合せによって返されるデータには、問合せ対象のデータの配列の次の要素から生成された配列が含まれています。

    • 2番目から4番目の要素(範囲3 to 1)

    • 3番目から5番目の要素(範囲2 to 4)

    • 末尾の2つ前から末尾の1つ前までの要素(範囲last-1 to last-2)

    • 先頭の要素(索引0)

    • 再び先頭の要素(索引0)

    データの配列["1", "2", "3", "4", "5", "6", "7", "8", "9"]と照合すると、問合せ結果の配列は["2", "3", "4", "3", "4", "5", "7", "8", "1", "1"]になります。

    データの配列(0から配列サイズ-1まで)の範囲外の位置を指定する配列索引を使用しても、エラーは発生しません。指定されたパス式がデータと一致しないだけです。配列にそのような位置はありません。(配列ステップのみではなく、通常、SQL/JSONパス式の照合はこのルールに従っています。)

    これは、たとえば、要素が7個未満の配列に対して索引last-6を照合しようとした場合などです。6個の要素を持つ配列の場合、last5であるため、last-6は(0より小さい)無効な位置を指定することになります。

    また、任意の配列ステップをの配列に対して照合しようとする場合にも当てはまります。たとえば、配列ステップ[0][last]はどちらもデータ配列[]と一致しません。[]には先頭の要素がないため、ステップ[0]は一致しません。また、[]には索引-1 (配列の長さ-1)の要素がないため、ステップ[last]は一致しません。

    これは特に、ファンクションjson_transform以外に索引last+N (Nはゼロ以外)を使用した場合にも当てはまります。json_transformの場合、これは、既存の配列要素と照合するためではなく、既存の配列の変更時に新しい要素を挿入する位置を指定するために使用します。

    範囲指定は明示的な昇順の配列索引と等価であるため、範囲外の暗黙的な索引はデータと一致しません。明示的な索引と同様に、それらの索引は無視されます。

    これについて考えるもう1つの方法は、範囲指定は実際には所与のデータ配列の最も近い境界(0またはlast)で切り捨てられるということです。たとえば、配列["a", "b", "c"]と照合する場合、範囲指定last-3 to 12 to last+1およびlast-3 to last+1は、実際にはそれぞれ0 to 12 to 2および0 to 2に切り捨てられます。これらの範囲の(暗黙的な)範囲外の索引last-3 (ここでは-1)およびlast+1 (ここでは3)は無視されます。

  • 子孫ステップでは、2つの連続したピリオド(..)(ドット・ドットとも呼ばれる)の後にフィールド名を指定します(オブジェクト・ステップの場合と同じ構文)。

    これは、直前のステップ(前のステップがない場合はコンテキスト項目)と一致するオブジェクトまたは配列で、再帰的に子孫を参照します。

    各子孫レベルでは、各オブジェクトおよびオブジェクトである各配列要素について、指定された名前を持つすべてのフィールドの値が収集されます。収集されたフィールド値をすべて返します。

    たとえば、次の問合せとデータについて考えます。

    json_query(some_json_column, '$.a..z' WITH ARRAY WRAPPER)
    { "a" : { "b" : { "z" : 1 },
              "c" : [ 5, { "z" : 2 } ],
              "z" : 3 }
      "z" : 4 }

    この問合せでは[1,2,3]のような配列が返され、その要素は12および3になります。ドット・ドット(..)の直前のステップ、つまりフィールドa内の各フィールドzの値が収集されます。値4の最上位のフィールドzは、フィールドaの値内にないため一致しません

    フィールドaの値は、子孫が参照されるオブジェクトです。

    • zというフィールドがあり、その値(3)が収集されます。また、値がオブジェクトであるフィールドbもあります。これは、フィールドzの値(1)を収集するために子孫が参照されます。

    • 値が配列であるフィールドcもあり、この配列には、値(2)が収集されるフィールドzを持つオブジェクトである要素があります。

    したがって、収集されるJSON値は31および2です。これらの値は未定義の順序で配列にラップされます。可能性のある戻り値の1つは、[1,2,3]です。

  • フィルタ式(フィルタと短縮される)は、疑問符(?)の後カッコ(())で囲まれたフィルタ条件を記述します。条件が満たされるとフィルタが満たされ、trueが戻されます。

  • フィルタ条件では、その引数に述語(ブール型関数)が適用されます。次のいずれかが該当します。この例の、condcond1およびcond2が、フィルタ条件を表します。

    • ( cond ): カッコを使用してグループ化することで、前後にあるその他のフィルタ条件から、フィルタ条件condを1つのユニットとして区別します。

    • cond1 && cond2: cond1cond2論理積(and)で、両方を満たす必要があります。

    • cond1 || cond2: cond1およびcond2の包含的論理和(or)で、cond1cond2またはその両方を満たす必要があります。

    • ! ( cond ): cond否定を表し、condが満たされてはならないことを意味します。

    • exists (単純な相対パス式): 対象となるデータが存在する条件。

    • 比較(次のいずれか)

      • 相対パス式、その後に比較述語、その後にJSONスカラー値またはSQL/JSON変数のいずれか。

      • JSONスカラー値またはSQL/JSON変数のいずれか、その後に比較述語、その後に相対パス式

      • JSONスカラー値、その後に比較述語、その後に別のJSONスカラー値。

      • 相対パス式の後にhas substringstarts withlikelike_regexまたはeq_regexが続き、その後にJSON文字列またはSQL文字列(データベース・キャラクタ・セットからUTF8に自動的に変換される)にバインドされているSQL/JSON変数が続きます。

        • has substringは、一致するデータ値に部分文字列として指定された文字列が含まれていることを意味します。

        • starts withは、一致するデータ値に接頭辞として指定された文字列が含まれていることを意味します。

        • likeは、JSON文字列データ値が、SQL LIKE4キャラクタ・セット・セマンティクスを使用するSQL LIKEパターンとして解釈される指定された文字列と一致することを意味します。パターン内のパーセント記号(%)は、ゼロ個以上の文字に一致します。アンダースコア(_)は1文字と一致します。

          注意:

          SQLのLIKEの場合とは異なり、パス式の述語likeエスケープ文字は選択できません。常に文字「`」(グレイヴ・アクセント(U +0060)、バッククォートやバックティックとも呼ばれます)を使用します。

          20cより前のデータベース・リリースでは、パス式の述語likeにエスケープ文字はありません。そのようなリリースでは、likeパターンで文字「`」(グレイヴ・アクセント(U+0060))を使用しないようにすることをお薦めします。

        • like_regexは、JSON文字列データ値が、SQL LIKE4キャラクタ・セット・セマンティクスを使用するSQL REGEXP LIKE正規表現パターンとして解釈される指定された文字列と一致することを意味します。

          like_regexはパターン一致比較の例外です。パターンが空のJSON文字列("")と一致するためです。

        • eq_regexlike_regexと同様ですが、次の2つの違いがあります。

          • eq_regexは、JSON文字列データ値全体に対する正規表現パターンと一致します。完全な文字列は、比較対象のパターンと一致する必要があります。like_regexは、JSON文字列の一部がパターンと一致する場合に満たされます。

          • eq_regexパターンが空のJSON文字列("")と一致しません。

        これらのすべての述語について、空の文字列("")のパターンは空の文字列のデータに一致します。また、like_regex以外のすべてで、空でない文字列であるパターンは空の文字列のデータと一致しませんlike_regexの場合、空でないパターンは空の文字列データと一致します。

      • 相対パス式の後にinが続き、その後に値リストが続きます。つまり、値が値リストにある値のいずれかになります。

      比較述語==<>!=<a id="fn_2" name="fn_2" href="#fn_2" onclick='footdisplay(2, "!= is an Oracle alias for the SQL/JSON standard comparison predicate <>.")'>脚注2<<=>=または>です(それぞれ等しい、等しくない、未満、以下、以上、より大きい)。

      SQL/JSON変数は、ドル記号($)の後に、json_existsPASSING句内にバインドされるSQL識別子の名前が続きます。

    • 値リストは、(の後に、1つ以上のスカラー値およびSQL/JSON変数をカンマ(,)で区切ったリストが続き、その後に)が続く形式です。

    このように、フィルタ条件で使用できる述語は、&&||!exists==<>!=<<=>=>およびinです。

    例を示します。次の基準の両方が満たされると、フィルタ条件(a || b) && (!(c) || d < 42)が満たされることになります。

    • abのフィルタ条件のうち、少なくともいずれか1つが満たされる: (a || b)

    • フィルタ条件cが満たされない、は数値dが42以下になる、またはその両方が当てはまる: (!(c) || d < 42)

    比較述語!&&||は、この順番で優先度が高くなります。カッコは、常にグループ化を制御するために使用できます。

    グループ化のためのカッコを使用しないと、前述の例はa || b && !(c) || d < 42のようになり、次の条件の少なくともいずれか1つが満たされた場合に、この式が満たされます。

    • 条件b && !(c)が満たされる。つまり、条件b!(c)がそれぞれ満たされることを意味する(つまり、条件cは満たされない)。

    • 条件aが満たされる。

    • 条件d < 42が満たされる。

比較の少なくとも一方をSQL/JSON変数にすることはできません。比較のデフォルトのは、非変数側の型に基づいてコンパイル時に定義されます。型指定項目メソッドを使用すると、このデフォルトを別の型でオーバーライドできます。一致するデータの型は、比較のために自動的に変換され、決定された型(デフォルトまたは項目メソッドで指定)に適合します。たとえば、$.a > 5では5が数値であるため、数値の比較が課され、$.a > "5"では"5"が文字列であるため、文字列の比較が課されます。

注意:

Oracle SQLファンクションjson_textcontainsを使用すると、JSONデータの強力な全文検索を実行できます。単純な文字列のパターン一致のみが必要な場合は、かわりにパス式フィルタ条件をhas substringstarts withlikelike_regexまたはeq_regexのいずれかのパターン一致比較とともに使用できます。

パス式の例を次に示しますが、その意味も詳述しています。

  • $ - コンテキスト項目。

  • $.friends - コンテキスト項目オブジェクトのフィールドfriendsの値。ドル記号($)の直後のドット(.)は、コンテキスト項目がJSONオブジェクトであることを示します。

  • $.friends[0] - コンテキスト項目オブジェクトのフィールドfriendsの値である配列の最初の要素であるオブジェクト。大カッコ表記法は、フィールドfriendsの値が配列であることを示します。

  • $.friends[0].name - コンテキスト項目オブジェクトのフィールドfriendsの値である配列の最初の要素であるオブジェクトのフィールドnameの値。2番目のドット(.)は、配列friendsの最初の要素が(フィールドnameを持つ)オブジェクトであることを示します。

  • $.friends[*].name - コンテキスト項目オブジェクトのフィールドfriendsの値である配列のオブジェクトのフィールドnameの値。

  • $.*[*].name - コンテキスト項目オブジェクトのフィールドの配列値に含まれる各オブジェクトのフィールドnameの値。

  • $.friends[3, 8 to 10, 12] – 配列friends(コンテキスト項目オブジェクトのフィールド)の4番目、9番目から11番目、13番目の要素。要素は指定された順序で返されます(4番目、9番目、10番目、11番目、13番目)。

    照合する配列の要素が13個未満の場合、索引12は一致しません。照合する配列の要素が10個のみの場合、索引12と一致しないのみではなく、範囲8 to 10は実際には位置8および9 (要素9および10)に切り捨てられます。

  • $.friends[12, 3, 10 to 8, 12]: 配列friendsの13番目、4番目、9番目から11番目、および13番目の要素(この順序で)。要素は指定された順序で返されます。範囲10 to 8は、範囲8 to 10と同じ要素を同じ順序で指定します。13番目の要素(位置12)は2回返されます。

  • $.friends[last-1, last, last, last]: 配列friendsの末尾の1つ前、末尾、末尾および末尾の要素(この順序で)。末尾の要素は3回返されます。

  • $.friends[last to last-1, last, last]: 前述の例と同じです。範囲last to last-1は、範囲last-1 to lastと同じであり、末尾の1つ前から末尾の要素を返します。

  • $.friends[3].cars - 配列friendsの4番目の要素であるオブジェクトのフィールドcarsの値。ドット(.)は、4番目の要素が(フィールドcarsを持つ)オブジェクトであることを示します。

  • $.friends[3].* - 配列friendsの4番目の要素であるオブジェクトのすべてのフィールドの値。

  • $.friends[3].cars[0].year – 配列friendsの4番目の要素であるオブジェクトのフィールドcarsの値である配列の最初の要素であるオブジェクトのフィールドyearの値。

  • $.friends[3].cars[0]?(@.year > 2016) – 配列cars (配列friendsの4番目の要素であるオブジェクトのフィールド)の最初のオブジェクト。ただし、そのフィールドyearの値が2016よりも大きいか、2016よりも大きい数値に変換できるもの。"2017"などのyear値は、テストを満たす数値2017に変換されます。"recent"などのyear値は、一致せず、テストに失敗します。

  • $.friends[3].cars[0]?(@.year.number() > 2016) - 前と同じです。項目メソッドnumber()は数値に変換できる数値または文字列値のみを許可し、その動作は数値比較述語>によってすでに指定されています。

  • $.friends[3].cars[0]?(@.year.numberOnly() > 2016) - 前と同じですが、year値が数値の場合のみです。項目メソッドnumberOnly()は、year値が文字列数字("2017"など)である車を除外します。

  • $.friends[3]?(@.addresses.city == "San Francisco") – 配列friendsの4番目の要素であるオブジェクト。ただし、そのaddressesフィールドの値が、フィールドcityの値が文字列"San Francisco"のオブジェクトであるもの。

  • $.friends[*].addresses?(@city starts with "San ").zip - 住所cityの名前がSanで始まる、friendsのすべてのaddressesの郵便番号。(この場合、フィルタは最後のパス・ステップではありません。)

  • $..zip - 任意のレベルのzipフィールドのすべての値。

  • $.friends[3]?(@.addresses.city == "San Francisco" && @.addresses.state == "Nevada") – 配列friendsの4番目の要素であるオブジェクト。ただし、city"San Francisco"の住所に対する一致があり、state"Nevada"の住所に対する一致があるもの。

    注意: 論理積になったフィルタ条件は、必ずしも同じオブジェクトに該当する必要はありません。このフィルタでは、cityがSan Franciscoのオブジェクトがあるかどうかと、stateがNevadaのオブジェクトがあるかどうかがテストされます。cityがSan FranciscoでstateがNevadaの両方を備えたオブジェクトがあるかどうかはテストされませんJSON_EXISTSでのフィルタの使用を参照してください。

  • $.friends[3].addresses?(@.city == "San Francisco" && @.state == "Nevada") – 配列friendsの4番目の要素であるオブジェクト。ただし、そのオブジェクトに、city"San Francisco"への一致と、state"Nevada"への一致があること。

    前述の例とは異なり、この場合の論理積のフィルタ条件(フィールドcitystate)は、同じaddressesオブジェクトに適用されます。フィルタは、その外側にある特定のaddressesオブジェクトに適用されます。

関連項目:

14.2.2 SQL/JSONパス式の構文の緩和

暗黙的な配列のラップおよびアンラップを可能にするため、SQL/JSONパス式の基本的な構文は緩和されています。これは、データが発展して特定のJSON値のかわりにこのような値の配列が使用される(またはこの逆)場合、コード内のパス式を変更する必要がないことを意味します。例を示します。

SQL/JSONパス式の基本的な構文では、SQL/JSONパス式の基本的な構文を定義しています。サポートされている実際のパス式の構文では、この定義を次のように緩和します。

  • パス式のステップが配列を対象としている(予測している)が、実際のデータは配列を示していない場合、データは暗黙的に配列にラップされます。

  • パス式のステップが非配列を対象としている(予測している)が、実際のデータは配列を示している場合、配列タは暗黙的にアンラップされます。

この緩和により、次の省略が可能になります。すなわち、[*]は、オブジェクト・アクセッサ.の前にあり、オブジェクト・フィールド名が後ろに続く場合は常に省略でき、本質的に何の変更も行われません。この逆もまた真です。[*]は、オブジェクト・アクセッサ.の前に常に挿入でき、本質的に何の変更も行われません。

つまり、特定のオブジェクトの配列の各要素のフィールドpropの値を表すオブジェクト・ステップ[*].prop.propとして省略でき、単一オブジェクトのprop値を表すかのように見えるオブジェクト・ステップ.propは、オブジェクト・アクセッサが適用された配列の各要素のprop値も表します。

これは重要な機能です。なぜなら、データが発展して特定のJSON値のかわりにこのような値の配列が使用される(またはこの逆)場合、コード内のパス式を変更する必要がないからです。

たとえば、フィールドtypeおよびnumberを伴う単一オブジェクトが値であるフィールドPhoneを持つオブジェクトがデータにもともと含まれるときに、このデータが発展して電話の配列を表す場合、単一の電話番号と一致するパス式$.Phone.numberを引き続き使用できます。パス式$.Phone.numberは、番号を選択して単一の電話オブジェクトと一致することも、各番号を選択して電話オブジェクトの配列と一致することもできます。

同様に、データに両方の種類の表現が混在する(単一の電話オブジェクトを使用するデータ・エントリ、および電話オブジェクトの配列を使用するエントリ、または両方を使用するエントリさえも存在する)場合、同じパス式を使用して、これらの異なる種類のエントリの電話情報にアクセスできます。

次に、SQL/JSONパス式の基本的な構文の項のパス式の例を、相当する式の説明とともに示します。

  • $.friends – 次のいずれかのフィールドfriendsの値。

    • (単一の)コンテキスト項目オブジェクト。

    • ($[*].friendsに相当)コンテキスト項目配列内の各オブジェクト。

  • $.friends[0].name - 次のオブジェクトのいずれかのフィールドnameの値。

    • コンテキスト項目オブジェクトのフィールドfriendsの値である配列の最初の要素。

    • ($.friends.nameに相当)コンテキスト項目オブジェクトのフィールドfriendsの値。

    • ($[*].friends.nameに相当)コンテキスト項目配列の各オブジェクトのフィールドfriendsの値。

    • ($[*].friends[0].nameに相当)コンテキスト項目配列の各オブジェクトのフィールドfriendsの値である各配列の最初の要素。

    コンテキスト項目はオブジェクトでもオブジェクトの配列でもかまいません。後者の場合、配列内の各オブジェクトはフィールドfriendsについて照合されます。

    フィールドfriendsの値はオブジェクトでもオブジェクトの配列でもかまいません。後者の場合、配列内の最初のオブジェクトが使用されます。

  • $.*[*].name – 次のオブジェクトのいずれかのフィールドnameの値。

    • コンテキスト項目オブジェクトのフィールドの配列値の要素。

    • ($.*.nameに相当)コンテキスト項目オブジェクトのフィールドの値。

    • ($[*].*.nameに相当)コンテキスト項目配列のオブジェクトのフィールドの値。

    • ($[*].*[*].nameに相当)コンテキスト項目配列のオブジェクトのフィールドの配列値の各オブジェクト。

14.3 SQL/JSONパス式の項目メソッド

SQL/JSONパス式で使用できるOracle項目メソッドについて説明します。

項目メソッドは、このメソッドで終了するパス式(の残り)が対象とするJSONデータに適用されます。メソッドは、目的のデータを変換するために使用されます。パス式に渡されたSQLファンクションまたは条件は、対象データのかわりとして変換済データを使用します。項目メソッドのアプリケーションがフィルタとして機能し、この結果セットから対象データを削除する場合があります。

引数の型が間違っているなどのなんらかの理由で項目メソッドの変換が失敗した場合、パスは照合されず(参照データがない状態)、エラーは発生しません。このことは、特に、パス式が渡されるSQLファンクションまたは条件のエラー句でこのようなエラーが処理されないことを意味します。

項目メソッドは、対象のJSONデータを(場合によっては他の) JSONデータに常に変換します。ただし、パス式を使用した問合せ(項目メソッドを使用しているかどうかにかかわらず)では、JSONデータをサポートしていないSQLデータ型としてデータを返すことができます。これはjson_valueの問合せまたは同等のドット表記法の問合せの場合です。

  • SQL/JSONファンクションjson_query (またはjson_queryのセマンティクスを持つjson_table列の式)の戻り値は、常にSQLデータ型JSONVARCHAR2CLOBまたはBLOBJSONデータです。対象データがJSON型である場合、デフォルトの戻りデータ型はJSONです。それ以外の場合はVARCHAR2です。

  • SQL/JSONファンクションjson_value (またはjson_valueのセマンティクスを持つjson_table列の式)の戻り値は、常にJSON型以外のSQLデータ型(スカラー型、オブジェクト型またはコレクション型)です。JSONデータは返されません。パス式はJSONデータを対象とし、項目メソッドは対象のJSONデータをJSONデータに変換しますが、json_valueは、JSONデータを必ずしもサポートしないデータ型のスカラーSQL値に結果のJSONデータを変換します。

  • 項目メソッドを使用するドット表記法問合せでは、RETURNING句を使用してjson_valueを暗黙的に適用します。この句は、項目メソッドの対象となる(場合によっては変換される) JSONデータに対してスカラーSQL型を指定します。このため、項目メソッドを使用するドット表記法問合せは、常にSQLスカラー値を返します。

配列への項目メソッドの適用

項目メソッドcount()size()およびtype()を除き、項目メソッドが配列を対象とする場合、このメソッドは配列自体ではなく各配列要素に適用されます。これらのアプリケーションの結果は、配列のかわりに複数の値として返されます。つまり、一致の結果セットには、対象の配列ではなく、変換された各配列要素が含まれています。

(このことは、実質的に、オブジェクト・ステップに対して非配列が想定される場合の配列の暗黙のアンラップと同様です。)

たとえば、$.a.method()は、項目メソッドmethod()を配列aの各要素に適用してその要素を変換し、配列のかわりに使用します。

  • 戻り型としてSQLコレクション型(varrayまたはネストされた表)を指定するjson_value問合せの場合、そのコレクション型インスタンスが返されます。これは、コレクション型定義に関して型の不一致がある場合を除いて、各配列要素に項目メソッドを適用した結果のJSON配列に対応しています。

  • 他のSQL型を返すjson_value問合せの場合、SQLのNULLが返されます。これは、項目メソッドを配列要素にマッピングすると複数の戻り値が生成され、それがjson_valueの不一致を表すためです。

  • json_queryまたはjson_queryセマンティクスを持つjson_table列式に対して、ラッパー句を使用すると変換されたすべての配列要素値を配列として取得できます。たとえば、次のような問合せになります。

    SELECT json_query('["alpha", 42, "10.4"]', '$[*].stringOnly()'
                      WITH ARRAY WRAPPER)
      FROM dual;

    ["alpha", "10.4"]というJSON配列を返します。返されるSQLデータ型は、対象がJSONVARCHAR2(4000)CLOBまたはBLOBであるJSONデータと同じです。

これに関して、項目メソッドcount()size()およびtype()例外です。配列に適用する場合、これらの項目メソッドは、その要素を処理するかわりに配列をそのまま扱います。次に例を示します。

SELECT json_value('[19, "Oracle", {"a":1},[1,2,3]]', '$.type()') FROM dual;

単一のVARCHAR2'array'を返します。json_valueはデフォルトでVARCHAR2(4000)を返します。

類似した問合せですが、json_valueではなくjson_queryを使用すると、入力JSONデータに使用されるSQLデータ型(JSONVARCHAR2(4000)CLOBまたはBLOB)の、単一のJSON文字列"array"を返します。

注意:

json_value (オブジェクト型またはコレクション型以外のSQL戻り型)で行われる内容と同じことが、簡単なドット表記法問合せでも行われます。ドット表記法構文に項目メソッドが存在すると、常にjson_queryではなくjson_valueのセマンティックスとなります。これは単一のスカラーSQL値を生成します(SQLのORDER BYGROUP BY、比較、または結合操作で使用できます)。ただし、配列値に適用される項目メソッドは、json_valueのセマンティクスによって拒否される複数の値を生成します。SQLのNULLが返されます。

項目メソッドの説明

次の項目メソッドの説明では、対象となるJSON値が特定のSQLデータ型の値として解釈される場合があると記述されています。つまり、json_valueのセマンティクスを持つ問合せでは、そのSQLデータ型を使用したRETURNING句によって制御される場合と同様に処理されます。

たとえば、項目メソッドstring()は句RETURNING VARCHAR2(4000)を含むjson_valueと同じようにターゲットを解釈します。ブール値はstring()によって"true"または"false"として扱われ、null値は"null"として扱われ、数値は正準な文字列形式で表されます。

名前に"only"が含まれるデータ型の変換メソッドは、"only"が含まれない名前の対応するメソッドと同じです。前者は、特定の型であるJSON値(numberなど)のみ を関連のSQLデータ型(NUMBERなど)に変換する点が異なります。名前に"only"が含まれないメソッドは、任意のJSON値を指定したSQLデータ型に変換できます。("only"メソッドで配列を対象とする場合、変換は通常どおり、各配列要素に適用されます。)

  • abs(): 対象となるJSONの数値の絶対値。SQLファンクションABSの使用に対応します。

  • avg(): 対象となるすべてのJSON数値の平均値。項目メソッドnumber()が、場合によっては複数の各値に暗黙的にまず適用されます。それらの平均値(1つのNUMBER値)が返されます。数値に変換できない対象のJSON値は無視されます。

  • binary(): 対象となるJSON値のSQL RAWの解釈。JSON型として格納されているJSONデータのみが一致します。

  • boolean(): 対象となるJSON値のSQL VARCHAR2(20)の解釈。

  • booleanOnly(): JSONのブール値である場合、対象となるJSONデータのSQL VARCHAR2(20)の解釈。それ以外の場合、一致はありません。フィルタとして機能し、JSONのブール値の場合のみ一致が可能です。

  • ceiling(): 最も近い整数に切り上げられた対象となるJSONの数値。SQLファンクションCEILの使用に対応します。

  • count(): 対象のJSON値の数(型にかかわらず)。

  • date(): 対象となるJSON文字列のSQLのDATEでの解釈。対象の文字列データは、サポートされているISO 8601形式の日付または日時である必要があります。それ以外の場合は一致しません。JSON文字列にISO 8601の日時形式がある場合、SQLのDATEインスタンスはその時間コンポーネントを切り捨てられます(0に設定されます)。

  • dateWithTime(): ISO 8601の日時形式の時間コンポーネントがSQLのDATEインスタンスに保存されることを除き、date()と同様です。

  • double(): 対象となるJSON文字列または数値のSQL BINARY_DOUBLEでの解釈。

  • dsInterval(): 対象となるJSON文字列のSQLのINTERVAL DAY TO SECONDでの解釈。対象となる文字列データは、サポートされているISO 8601継続時間形式のいずれかである必要があります。それ以外の場合、一致はありません。
  • float(): 対象となるJSON文字列または数値のSQL BINARY_FLOATでの解釈。JSON型として格納されているJSONデータのみが一致します。

  • floor(): 最も近い整数に切り捨てられた対象となるJSONの数値。SQLファンクションFLOORの使用に対応します。

  • length(): 対象となるJSON文字列内の、SQL NUMBERとして解釈される文字数。

  • lower(): 対象となるJSON文字列内の文字に対応する小文字の文字列。

  • maxNumber(): 対象となるすべてのJSON数値の最大値。項目メソッドnumber()が、場合によっては複数の各値に暗黙的にまず適用されます。それらの最大値(1つのNUMBER値)が返されます。数値に変換できない対象のJSON値は無視されます。

  • maxString(): 照合順序を使用した、対象となるすべてのJSON文字列の最大値。項目メソッドstring()が、場合によっては複数の各値に暗黙的にまず適用されます。これらの最大値(単一のVARCHAR2値)が返されます。文字列に変換できない対象のJSON値は無視されます。

  • minNumber(): 対象となるすべてのJSON数値の最小値。項目メソッドnumber()が、場合によっては複数の各値に暗黙的にまず適用されます。それらの最小値(1つのNUMBER値)が返されます。数値に変換できない対象のJSON値は無視されます。

  • minString(): 照合順序を使用した、対象となるすべてのJSON文字列の最小値。項目メソッドstring()が、場合によっては複数の各値に暗黙的にまず適用されます。これらの最小値(単一のVARCHAR2値)が返されます。文字列に変換できない対象のJSON値は無視されます。

  • number(): 対象となるJSON文字列または数値のSQL NUMBERの解釈。

  • numberOnly(): JSONのブール値である場合、対象となるJSONデータのSQL NUMBERの解釈。それ以外の場合、一致はありません。フィルタとして機能し、JSONの数値の場合のみ一致が可能です。

  • size(): 複数のJSON値が対象である場合、各対象値にsize()を適用した結果です。そうでない場合は、次のようになります。

    • 単一の対象値がスカラーの場合は1です。

    • 単一の対象値が配列の場合は、配列の要素数です。

    • 単一の対象値がオブジェクトの場合は1です。

    この項目メソッドは、json_queryjson_valueおよびjson_tableとともに使用できます。配列のデータに適用される場合、配列要素に対する暗黙的な反復は発生しません。結果の値は配列要素の数に一致します。(暗黙的反復のルールに対する例外。)

  • string(): 対象となるスカラーのJSON値のSQL VARCHAR2(4000)の解釈。

  • stringOnly(): JSON文字列である場合、対象となるスカラーJSON値のSQL VARCHAR2(4000)の解釈。それ以外の場合、一致はありません。フィルタとして機能し、JSON文字列の場合のみ一致が可能です。

  • sum(): 対象となるすべてのJSON数値の合計値。項目メソッドnumber()が、場合によっては複数の各値に暗黙的にまず適用されます。それらの合計値(1つのNUMBER値)が返されます。数値に変換できない対象のJSON値は無視されます。

  • timestamp(): 対象となるJSON文字列のSQL TIMESTAMPの解釈。対象の文字列データは、サポートされているISO 8601形式の日付または日時である必要があります。それ以外の場合は一致しません。<a id="fn_3" name="fn_3" href="#fn_3" onclick='footdisplay(3, "Applying item method timestamp() to a supported ISO 8601 string <ISO-STRING> has the effect of SQL sys_extract_utc(to_utc_timestamp_tz(脚注3

  • type(): SQL VARCHAR2(20)値として解釈される、対象となるデータのJSONデータ型の名前。この項目メソッドは、json_queryjson_valueおよびjson_tableとともに使用できます。配列のデータに適用される場合、配列要素に対する暗黙的な反復は発生しません。結果の値は"array"となります。(暗黙的反復のルールに対する例外。)

    • nullの値の場合、"null"

    • ブール値(trueまたはfalse)の場合、"boolean"

    • 数値の場合、"number"

    • 文字列の場合、"string"

    • 配列の場合、"array"

    • オブジェクトの場合、"object"

    • SQLのBINARY_DOUBLE値に対応する数値の場合、"double"。(JSON型のデータの場合のみ。)

    • SQLのBINARY_FLOAT値に対応する数値の場合、"float"。(JSON型のデータの場合のみ。)

    • SQLのRAW値に対応する値の場合、"binary"。(JSON型のデータの場合のみ。)

    • SQLのDATE値に対応する値の場合、"date"。(JSON型のデータの場合のみ。)

    • SQLのTIMESTAMP値に対応する値の場合、"timestamp"。(JSON型のデータの場合のみ。)

    • SQLのINTERVAL DAY TO SECOND値に対応する値の場合、"daysecondInterval"。(JSON型のデータの場合のみ。)

    • SQLのINTERVAL YEAR TO MONTH値に対応する値の場合、"yearmonthInterval"。(JSON型のデータの場合のみ。)

  • upper(): 対象となるJSON文字列内の文字に対応する大文字の文字列。

  • ymInterval(): 対象となるJSON文字列のSQLのINTERVAL YEAR TO MONTHでの解釈。対象となる文字列データは、サポートされているISO 8601継続時間形式のいずれかである必要があります。それ以外の場合、一致はありません。

項目メソッドbinary()boolean()booleanOnly()date()dateWithTime()dsInterval()float()length()lower()number()numberOnly()string()stringOnly()timestamp()upper()およびymInterval()は、SQL/JSON標準に対するOracleの拡張機能です。その他の項目メソッドのabs()ceiling()double()floor()size()およびtype()は標準に含まれています。

項目メソッドavg()count()maxNumber()minNumber()maxString()minString()およびsum()は、集計項目メソッドです。各対象値に個別に作用するのではなく、対象値すべてに対して作用します。たとえば、パス式が数値に変換できる複数の値を対象としている場合、sum()はそれらの数値の合計を返します。

パス式が配列を対象としている場合、集計項目メソッドをその配列に適用すると、配列は単一の値として処理されます。配列要素が暗黙的に反復されることはありません。たとえば、count()は対象となる配列を1つの値としてカウントし、size()は配列要素のサイズではなく配列のサイズを返します。

集計項目メソッドを配列要素に適用する場合は、ワイルドカード*を使用して、それらの要素を明示的に反復処理する必要があります。たとえば、指定された文書のLineItemsフィールドの値が配列の場合、$.LineItems.count()1を返しますが、$.LineItems[*].count()は配列要素の数を返します。

集計項目メソッドは、それが含まれるパス式(ドット表記法)と同様に、一度に1つのJSON文書に適用されます。パス式によってその文書内で対象となる複数の値を集計します。問合せは、各文書について1つの行を返します。複数の文書の情報を集計するのではなく、SQL集計関数と同様に、すべての文書に対して1つの行を返します。例14-1例14-2を参照してください。

項目メソッドおよびJSON_VALUEのRETURNING句

一部の項目メソッドは、対象のJSONデータをSQLのデータ型であるかのように解釈するため、RETURNING句のかわりにjson_valueとともに使用できます。また、列型を指定するかわりにjson_tableとともに使用できます。つまり、項目メソッドは、抽出されたJSONデータに返されるSQLデータ型を指定するために使用できます

このような項目メソッドをjson_valueRETURNING句またはjson_tableの列型指定とともに使用することもできます。抽出されたJSONデータに使用するSQLデータ型が項目メソッドおよびjson_value RETURNING句またはjson_table列タイプの両方によって制御されている場合はどうなりますか。

  • 2つのデータ型に互換性がある場合、RETURNING句または列のデータ型が使用されます。これらのために、VARCHAR2VARCHAR2CLOBの両方と互換性があります。

  • これらのデータ型に互換性がない場合、静的コンパイル時にエラーが発生します。

表14-1 型変換項目メソッドとRETURNINGの型の互換性

項目メソッド 互換性のあるRETURNING句のデータ型
string()stringOnly()minString()またはmaxString() VARCHAR2またはCLOB (string()がJSON null値に対してSQL NULLを返すことを除く)
number()numberOnly()avg()sum()count()minNumber()またはmaxNumber() NUMBER
double() BINARY_DOUBLE
float() BINARY_FLOAT
date() DATE (ゼロに設定された)切り捨てられた時間コンポーネントを含み、RETURNING DATE TRUNCATE TIMEに対応
dateWithTime() DATE (時間コンポーネントを含み、RETURNING DATE PRESERVE TIMEに対応)
timestamp() TIMESTAMP
ymInterval() INTERVALYEARTOMONTH
dsInterval() INTERVALDAYTOSECOND
boolean()またはbooleanOnly() VARCHAR2
binary() RAW

RETURNING句または列指定を使用すると、文字データの長さ、および数値データの精度とスケールを指定できます。これにより、対象データの比較のために項目メソッドで指定されるものよりも、抽出に対して正確にSQLデータ型を割り当てることができます。

たとえば、項目メソッドstring()およびRETURNING VARCHAR2(150)を使用する場合、戻されるデータのデータ型はVARCHAR2(150)で、VARCHAR2(4000)ではありません。

例14-1 各文書のフィールド値の集計

この例では、項目メソッドavg()を使用して、JSON文書のすべてのLineItems要素のQuantityフィールドの値を集計し、各文書の平均を個別の結果行として返します。

SELECT json_value(po_document, 
                  '$.LineItems[*].Quantity.avg()')
  FROM j_purchaseorder;

例14-2 すべての文書のフィールド値の集計

この例では、SQLファンクションavgを使用して、すべてのJSON文書の明細項目Quantityの平均値を集計し、文書コレクション全体の平均を単一行として返します。指定された文書のすべての明細項目の平均数量は、項目メソッドavg()を使用して計算されます。

SELECT avg(json_value(po_document, 
                      '$.LineItems[*].Quantity.avg()'))
  FROM j_purchaseorder;

14.4 比較における型

SQL/JSONパス式のフィルタ条件での比較は、コンパイル時に静的に型を指定します。比較のオペランドの有効な型が同じであると認識されない場合、型キャストでの調整が行われることがあります。

SQL/JSONパス式がJSONデータを対象としているため、比較のオペランドはJSON値です。JSON値の型の比較は単純です。JSONのデータ型である文字列、数値、null、オブジェクトおよび配列は、相互に排他的であり、比較できません。

ただし、比較オペランドはSQLデータ型の値として解釈(基本的にキャスト)されることがあります。たとえば、number()などの一部の項目メソッドが使用されている場合などです。この項は、このような有効値の型チェックに対応しています。

onlyが付く項目メソッドのいずれかを使用して、明示的にこのような型キャストを回避できます。たとえば、メソッドnumberOnly()を使用すると、数値への暗黙的な型キャストが回避されます。

SQLは静的に型を指定する言語のため、型はコンパイル時に決定されます。同じことがSQL/JSONパス式に適用され、特にフィルタ条件での比較にも適用されます。これは、評価方法に関係なく(索引、マテリアライズド・ビューおよびインメモリー・スキャンなどの機能を使用するかどうかに関係なく)、問合せに対して同じ結果が得られるという意味です。

これを実現するには、次のようにします。

  • 両方のオペランドの型が認識されており同じである場合、型チェックが実行されます。

  • 両方のオペランドの型が不明の場合、コンパイル時にエラーが発生します。

  • 1つのオペランドの型が認識されていて、もう一方の型が不明な場合、後者のオペランドは前者の型にキャストされます。

    たとえば、$.a?(@.b.c == 3)$a.b.cの型がコンパイル時に不明とします。パス式は、$.a?(@.b.c.number() == 3)としてコンパイルされます。実行時に、$a.b.cと一致するデータの数値へのキャストが試行されます。文字列値"3"は数値3にキャストされ、比較が実行されます。脚注4

  • 両方のオペランドの型は認識されており、これらが異なっている場合、一方の型のもう一方の型へのキャストが試行されます。詳細を次に示します。

次の組合せで使用される比較オペランドの調整の試行が、型キャストによって行われます。他のオペランドと互換性のある型にするために、型キャストの項目メソッドがオペランドのいずれかに暗黙的に適用されます。

  • doubleと比較される数値 — double()は暗黙的に通知に適用され、これをdouble値にします。

  • floatと比較される数値 — float()は暗黙的に数値に適用され、これをfloat値にします。

  • 日付と比較されるサポートされているISO 8601形式の文字列 — date()は暗黙的に文字列に適用され、これを日付値にします。このため、UTCタイムゾーン(協定世界時、ゼロのオフセット)はデフォルトとして使用され、文字列で指定された任意のタイムゾーンを考慮します。

  • タイムゾーンが含まれていないタイムスタンプと比較される、サポートされているISO 8601形式の文字列 — timestamp()が暗黙的に文字列に適用され、これをタイムスタンプ値にします。このため、UTCタイムゾーン(協定世界時、ゼロのオフセット)はデフォルトとして使用され、文字列で指定された任意のタイムゾーンを考慮します。

次の組合せで使用される比較オペランドは調整されずコンパイル時のエラーが発生します。

  • number、doubleまたはfloatとnumber、doubleまたはfloat以外の型の比較。

  • ブールとブール以外の型の比較。

  • 文字列がサポートされているISO 8601形式である場合を除く、文字列と比較される日付またはタイムスタンプ。

  • dateと(サポートされるISO 8601形式の)文字列以外の日付ではない型の比較。

  • 文字列以外の非タイムスタンプ型(サポートされているISO 8601形式)と比較されるタイムスタンプ(タイムゾーンがある場合とない場合)。

  • タイムスタンプとタイムゾーン付きのタイムスタンプの比較。
  • JSON null以外の任意の型と比較されるJSON null型。



脚注の凡例

脚注1: 範囲指定のtoは、配列のslice演算子と非公式に呼ばれることがあります。
脚注2: !=は、SQL/JSON標準比較述語<>のOracle別名。
脚注3: 項目メソッドtimestamp()をサポートされているISO 8601文字列<ISO-STRING>に適用すると、SQL sys_extract_utc(to_utc_timestamp_tz(<ISO-STRING>))の効果があります。
脚注4: ここでこのようなキャストを回避するには、項目メソッドnumberOnly(): $.a?(@.b.c.numberOnly() == 3)を適用します。文字列値"3"を含むデータは一致せず、フィルタで除外されます。