6 JSONリレーショナル二面性ビューに格納されたJSONデータ

二面性ビューの基礎となる表に格納されたJSONデータ型の列では、ビューでサポートされるドキュメントに任意の種類(スカラー、オブジェクト、配列)のJSON値を生成できます。この格納されたJSONデータは、(特定の形式およびフィールド・タイプを適用するために)スキーマレスまたはJSONスキーマベースにできます。

JSONデータを二面性ビュー(より正確には、基礎となる表)に格納するかどうか、格納する場合は、その構造およびタイプを適用するかどうかは、ビューを定義する際に考慮する必要がある設計の選択肢です。

二面性ビューでサポート(生成)されるJSONドキュメントに関与する一部のJSONデータを格納することで、ビューを定義する構成要素の粒度および複雑度を選択できます。言い換えると、基礎となるデータに必要な正規化の度合いを選択できます。選択肢が異なれば、トレードオフも異なります。

JSONリレーショナル二面性ビューは、基礎となる表データに基づくJSONドキュメントのセットをサポートします。JSONドキュメントは、必要に応じてこの表データから自動的に生成されます。

ドキュメント中心のアプリケーションでは、二面性ビューでサポートされるJSONドキュメントを、単一のJSON型の列に格納されているかのように、アクセス、更新、その他で使用します。アプリケーションでは、JSONドキュメントの列のみが表示されます。

同時に、リレーショナル/表中心のアプリケーションは、同じ基礎となる表データを直接、アクセス、更新、その他で使用できます。二面性: ドキュメントまたは表データへの変更は、他に自動的に反映されます(これは、データを共有する複数のドキュメントおよび表にまたがる場合です)。

通常、二面性ビューの基礎となる表データは完全に正規化されるため、表の列にはスカラーSQLデータ型の値のみが含まれます。

完全な正規化により、様々な種類の二面性ビューをサポートするために複数の表からのデータを組み合せることに関して柔軟性が最も高くなります(より一般的には、二面性ビューへの使用を除いた、ある表データを他の表データと組み合せることに関してです)。

また、重要な特定のユースケースでは、ドキュメント中心のアプリケーションからJSONドキュメントとして既存のリレーショナル表のデータにアクセスできます。

一方、正規化の度合いが大きいほど、表が多くなるため、データの挿入時に分解が増え、問合せ時に結合が増えます。通常、アプリケーションが複雑なオブジェクト全体にアクセスする場合、正規化が大きいほどパフォーマンスに悪影響を与える可能性があります。

特定の種類(構造およびタイプ)のJSONドキュメントの生成において、二面性ビューの基礎となる表の構成要素の列は材料を指定するものと考えることができ、二面性ビュー定義はレシピを指定するものと考えることもできます。

シンプルな基本材料のみを使用して、調理レシピは"ゼロから"である必要はありません。同時に、レシピは、あらかじめ集められた出来合いの"ミックス"に水を加えるだけのようにシンプルである必要もありません。基本(卵)から複合(ケーキ・ミックス)まで、材料ごとに様々な可能性があります。

たとえば、レシピで材料の1つとして"サラダ・ドレッシング"を必要とする場合、ボトルからの既製品にすることも、オリーブ・オイルや酢などのさらに基本材料を組み合せて作成することもできます。また、サラダ・ドレッシングの材料を構成する材料自体が、マヨネーズ、マスタードまたは調合されたスパイスのミックスなど、複合(出来合い)であってもかまいません。

二面性ビューの定義/実装に使用される材料と、それがサポートするJSONドキュメントについても同様です。

二面性ビューの基礎となる表がSQLスカラー値として完全に正規化されている場合、レシピの材料はできるだけシンプルで基本です(スカラー値はアトミック(不可分)です)。

ただし、表の列の一部(または全部)では、かわりにJSON型のデータを格納でき、そのデータはスカラーでも複合でもかまいません。場合によっては、二面性ビューに格納されている(小さい)JSONドキュメント全体を、生成された大きいドキュメントの一部として含めることが理にかなっています。これは、二面性ビューのレシピで複合材料をいくつか使用することに相当します。

Oracle Databaseでは、JSONデータ(ドキュメント)をJSONデータ型の列に格納でき、それらのドキュメントの任意の部分(任意のフィールド)を選択的に更新することも、一度にドキュメント全体を置換することもできます。

これは、二面性ビューの一部を定義するために使用される、格納されたJSON型の列のデータにも当てはまります。また、基礎となる表の他の列と同様に、JSON型の列は、様々な二面性ビュー間で共有できるため、様々な結果の(生成された)JSONドキュメントで共有できます。

デフォルトでは、JSONドキュメントは自由形式です。その構造およびタイプは、特定のパターン/スキーマによって定義されたり、準拠を強制されることはありません。この場合、アプリケーションは必要に応じてドキュメントの形式やタイプを簡単に変更できます。

一方、JSONスキーマを使用して、JSON型の列のデータにタイプおよび構造を適用できます。JSONスキーマを使用すると、次のような全面的な制御が可能です:

  1. 値がまったく定義されていないフィールドから、値が厳密に定義されているフィールドまで。

  2. スカラーJSON値から、大規模で複雑なJSONオブジェクトおよび配列まで。

  3. 単純なタイプ定義からJSON言語タイプの組合せまで。次に例を示します。

    • JSONスキーマのセットanyOfallOfまたはoneOfを満たす値

    • 特定のJSONスキーマを満たさない(not)値

型範囲の一端の例として、小さいJSONスキーマをJSON型の列に適用して、そのデータが特定のJSON言語のスカラー型であることを要求できます。

対応するOracle JSON言語のスカラー型は、二面性ビュー定義で使用できるSQLスカラー型ごとに存在します。その結果として、JSONスキーマの型指定は、SQLの型指定と同じようにきめ細かくできます。

たとえば、JSON型の列にチェック制約として適用した場合、このJSONスキーマでは、JSON文字列である値: {"type": "string"}のみを使用できます。その効果は、SQL型VARCHAR2の列を使用する場合と同様です。また、このスキーマでは、JSON日付(Oracle JSON言語のスカラー型)である値: {"extendedType": "date"}のみを使用できます。その効果は、SQL型DATEの列を使用する場合と同様です。

ノート:

二面性ビュー定義で、特定のJSONスカラー型(日付、文字列など)のデータのみを保持するようにJSONスキーマによって制約されるJSON型の列を使用すると、対応するSQLスカラー型(DATEVARCHAR2など)の列を使用するのと、ビューでサポートされるJSONドキュメントで同じ効果があります。

ただし、このようなJSON型データに直接作用するコードは、必ずしもこの対応を認識して考慮するとはかぎりません。データのSQL型は結局、JSONであり、DATEVARCHAR2などではありません。SQLスカラー・データ型の値としてJSONスカラー値を抽出するには、コードでSQL/JSONファンクションjson_valueを使用する必要があります。『Oracle Database JSON開発者ガイド』「SQL/JSONファンクションJSON_VALUE」を参照してください。

二面性ビューの基礎となる表で、基本材料(SQLスカラーの列)を使用する場合と、複合材料(JSON型の列)を使用する場合の間でのトレードオフの一部をまとめてみます。

  1. 組合せの柔軟性。最もきめ細かい組合せには、列がすべてSQLスカラーである完全に正規化された表を使用します。

  2. ドキュメントのタイプおよび構造の柔軟性。任意の時点でのJSONフィールド値の柔軟性を最大限に高めるためには、また時間経過に伴う変化(進化)に対しても、JSONスキーマ制約のないJSON型の列を使用します。

  3. フィールド定義の粒度。最も細かい粒度では、二面性ビューでサポートされるドキュメント内のどこにフィールドがあるかに関係なく、JSONフィールドごとに1列必要です。(列がJSON型の場合でも、フィールド値はJSONオブジェクトまたは配列である可能性があります。)

アプリケーションが様々な種類のドキュメント間で複雑なJSONデータを共有することが理にかなっている場合や、他のドキュメントと、またはSQLスカラーとしてリレーショナル・データと、その複雑なデータの一部のみを組み合せる必要がないと想定する場合は、その複雑なデータの基礎となる列にJSONデータ型を使用することを検討します。

つまり、このようなユースケースでは、JSONドキュメントを構成するスカラー値を共有するのではなく、JSONドキュメントを共有することを検討します。さらに言い換えれば、二面性ビューのレシピでもっと複合材料を使用することを検討してください。

列データの粒度(列内のデータの複雑度)により、更新操作およびETAGチェックの粒度(オプティミスティックな同時実行性制御の場合)も決まることに注意してください。このような操作の最小単位は、二面性ビューの基礎となる個々のです。JSON型の列内の個々のフィールドに注釈を付けることはできません。

更新操作は、特定のJSON型の列のデータに含まれる特定のフィールドに選択的に適用できますが、特定のビューで使用できる更新操作の制御は、基礎となる列または表全体(これより小さいものはありません)のレベルで定義されます。そのため、よりきめ細かい更新またはETAGチェックが必要な場合は、JSONデータの関連部分をそれぞれの列に取り出す必要があります。

関連項目:

6.1 フレックス列: 二面性ビューのスキーマの柔軟性と展開

JSONリレーショナル二面性ビューの基礎となる表内のフレックス列では、その表によって生成されたドキュメント・オブジェクトのフィールドを追加および再定義できます。これにより、二面性ビューおよびサポートするドキュメントに対して、ある種のスキーマの柔軟性が提供されます。

二面性ビューの基礎となる表には、任意の数のJSON型の列を含めることができます。フレックス列として指定できるのは、表ごとに1列までです。

どの表でも、通常、JSON列は柔軟性のあるデータを提供します。デフォルトでは、そのタイプと構造はいかなる方法でも(たとえば、JSONスキーマによって)制約/指定されません。

二面性ビューのフレックス列として指定されているJSON列の特長は次のとおりです:

  • 列値は、JSONオブジェクトまたはSQL NULLである必要があります。

    (この制限は、非フレックスJSON型の列には適用されません。この値には、スカラー、配列またはオブジェクトのいずれかのJSON値を指定できます。)

  • 読取りでは、フレックス列に格納されているオブジェクトはネストされていません。結果となるドキュメント・オブジェクトに、そのフィールドが展開されます

    つまり、フレックス列の表によって生成されるオブジェクトの一部のフィールドの値として、格納されているオブジェクトは含まれません。かわりに、格納されているオブジェクトの各フィールドが、そのドキュメント・オブジェクトに含まれます。

    (非フレックスのJSON型の列内の任意の値(オブジェクト、配列またはスカラー)はそのまま含まれ、オブジェクトはネストされません。)

    たとえば、表tab1のフレックス列の特定の行のオブジェクトにフィールドfooおよびbarがある場合、その行に対応する二面性ビュー・ドキュメントでは、tab1から生成されるオブジェクトにも、fooおよびbarのフィールドが含まれます。

  • 書込みでは、ドキュメント・オブジェクトのフィールドは格納されたオブジェクトに戻され、他の列でサポートされていないフィールドはフレックス列に自動的に追加されます。つまり、認識されないフィールドがJSONフレックス列のオブジェクトに"オーバーフロー"します。

    たとえば、新しいフィールドtotoが、フレックス列がある表に対応するドキュメント・オブジェクトに追加された場合、フィールドtotoが表によってまだサポートされていなければ、ドキュメントの挿入時にフィールドtotoがフレックス列のオブジェクトに追加されます。

二面性ビューに対してフレックスとして指定された列は、ビューに対してのみフレックスです。が属しているのは、通常のJSON型の列のみです(ただし、各行の値は単一のJSONオブジェクトまたはSQL NULLである必要があります)。

フレックス列のオブジェクトが読取り時にネストされず、フィールドが表の他の列によって生成されたフィールドに追加されるため、また、JSON列がデフォルトではスキーマレスであるため、フレックス列データへの変更によって、結果のドキュメント・オブジェクトの構造およびそのフィールドの一部のタイプが変更される場合があります。

実際に、JSONオブジェクトをサポートする表に任意のレベルでフレックス列を提供することで、二面性ビューのサポートされるドキュメントのタイプと構造をそのレベルで変更/展開できます。

列の表を介してフレックス列データを直接変更することで、二面性ビューのドキュメントのタイプと構造を変更できます。ただし、より重要なこととして、基礎となるリレーショナル列に対応していないフィールドを含むドキュメントを挿入または更新することで、これを実行できます。そのようなフィールドは、対応するフレックス列に自動的に追加されます。したがって、アプリケーションは、基礎となる表にフレックス列があるオブジェクトで、任意のフィールドを含むドキュメントを自由に作成できます。

ただし、フレックス列からオブジェクトをネスト解除すると、そのフィールドと、同じ表の他の列から導出されたフィールドとの間で名前の競合が発生する可能性があることに注意してください。このような競合は、フレックス列として機能しないJSON列では発生しません。

このため、格納されているJSONオブジェクトのネストを解除する必要がない場合、オブジェクト全体をフィールドの値として含めるだけで十分であれば、その列をフレックスとして指定しないでください。リレーショナル列でサポートされているドキュメント・オブジェクトにフィールドを追加できる必要がある場合は、フレックス列を使用します。

フレックス列のすべての行の値は、JSONオブジェクトまたはSQL値がNULLである必要があります。

SQL NULLと空のオブジェクト({})は同じように動作しますが、通常、これらはドキュメントのETAG値への関与が異なります。(フレックス列にNOCHECKの注釈を付けて、ETAG計算からそのデータを削除できます。フレックス列で列注釈[NO]UPDATE[NO]CHECKを使用することもできます。)

二面性ビュー定義では、SQLでキーワードAS FLEXを使用するか、GraphQLで注釈@flexを使用してビュー定義の列名に従って、JSON型の列をビューのフレックス列として指定します。

たとえば、この二面性ビューdv1のGraphQL定義では、表table1の列t1_json_colがフレックス列として指定されます。オブジェクト値のフィールドは、field1field2の兄弟として結果のドキュメントに含まれます。(JSONオブジェクトには未定義のフィールド順序があるため、二面性ビュー定義で表の列が指定される順序は重要ではありません。)

CREATE JSON RELATIONAL DUALITY VIEW dv1 AS
  table1 @insert @update @delete
    {_id       : id_col,
     t1_field1 : col_1,
     t1_json_col @flex,
     t1_field2 : col_2};

表が複数の二面性ビューの基礎となる場合、それらのビューでは当然、表の同じ列の一部またはすべてを使用できます。このような共有表の特定の列は、このような任意の数のビューに対してフレックスとして指定することも、それ以外を指定することもできます。

二面性ビューで列がフレックス列として使用されるということは、表を更新して列値に直接変更が行われる場合、列の値はJSONオブジェクト(またはSQL NULL)である必要があるということです。

また、同じ列が別の二面性ビューの基礎となる表で使用され、そのビューのフレックス列として指定されていない場合、そのビューでは列によって生成されたJSONフィールドが結果のドキュメントに展開されません。このビューでは、これらのフィールドを含むJSONオブジェクトがそのまま含まれます。つまり、フレックス列として指定することはビュー固有です。

静的ディクショナリ・ビュー*_JSON_DUALITY_VIEW_TABSBOOLEANの列HAS_FLEX_COLをチェックすると、二面性ビューの基礎となる特定の表にフレックス列があるかどうかを確認できます。静的ディクショナリ・ビュー*_JSON_DUALITY_VIEW_TAB_COLSBOOLEANの列IS_FLEX_COLをチェックすると、基礎となる表の特定の列がフレックス列かどうかを確認できます。『Oracle Databaseリファレンス』ALL_JSON_DUALITY_VIEW_TABSおよびALL_JSON_DUALITY_VIEW_TAB_COLSを参照してください。

二面性ビューの基礎となる表内のフレックスJSON列と非フレックスJSON列の両方のデータはスキーマレスの場合があり、デフォルトでも同様です。

ただし、JSONスキーマは、二面性ビュー定義の任意の場所で使用されるJSON型の列に適用して、その柔軟性を削除(ロック)できます。JSONスキーマは、二面性ビューによって生成またはサポートされているドキュメントに組み込むこともできます。

フレックス列内のオブジェクトのフィールドは結果のドキュメントに展開されるため、JSONスキーマをフレックス列に適用すると、DMLを使用してそのオブジェクトの各フィールドの個別の列をフレックス列の表に追加した場合と同様の結果になります。

実際には、JSONスキーマを適用することで、データの論理構造、つまりビューでサポートされているドキュメントの構造を変更します。スキーマの柔軟性は削除しますが、ストレージ構造(表)は変更しません。

フレックス列で生成されるフィールド名の競合

フレックス列のフィールドは、フィールドが指定されているオブジェクトに展開されるため、フィールド名の競合が発生する可能性があります。このようなことが発生するのは、次のような状況です:

  • 二面性ビューの基礎となる表が再定義され、新しい列が追加されます。二面性ビューが再定義され、新しい列に対応するJSONフィールドに、同じ表のフレックス列にすでに存在するフィールドと同じ名前が付与されます。

    問題: 非フレックス列に関連付けられたフィールド名は、フレックス列データのフィールドと同じになります。

  • フレックス列が直接更新され(つまり、ビューでサポートされているドキュメントを更新しない)、ビュー定義に対応するフィールドと同じ名前のフィールドが基礎となる同じ表の別の列に追加されます。

    問題: 非フレックス列に関連付けられたフィールド名は、フレックス列データでも使用されます。

  • 2つの二面性ビューdv1およびdv2は、同じ列jcolをフレックスとして使用して基礎となる表を共有します。dv1のみが、表の非フレックス列foocolを使用し、関連付けられたフィールドfooに名前を付けます。

    データがdv1に挿入され、列foocolが移入されます。これは、表に行を挿入するか、フィールドfooを含むドキュメントをdv1に挿入することによって発生する可能性があります。

    dv2にドキュメントを挿入すると、フィールドfooのJSON行がフレックス列に追加されます。

    問題: ビューdv2に問題はありません。ただし、ビューdv1のフィールド名fooは非フレックス列に関連付けられており、フレックス列データでも使用されます。

このような競合の発生を防止することはデータベースには実現できませんが、読取り(選択、取得、JSON生成)操作中に検出された場合に、それらの競合を処理する動作を指定できます。(このような競合はすべて、読取り時に検出されます。)

これは、フレックス列宣言の最後に次のキーワードを使用して実行します。エラーを発生させないすべてのケースでは、競合するフィールド名は非フレックス列から読み取られます。つまり、優先度は常に非フレックス列に指定されます。

GraphQL SQL 動作

(conflict: KEEP_NESTED)

KEEP [NESTED] ON [NAME] CONFLICT

(キーワードNESTEDおよびNAMEはオプションです。)

競合するフィールド名は、非フレックス列から読み取られます。フィールド_nameConflicts (予約名)が追加され、値とともにメンバーが競合する名前とその値であるオブジェクトがフレックス列から取得されます。

これがデフォルトの動作になります。

たとえば、特定のドキュメントについて、非フレックス・フィールドquantityに値100があり、フレックス列データに値"314"のフィールドquantityがある場合、非フレックス・フィールドquantityはその値100を保持し、フィールド_nameConflictsはメンバー"quantity":314を含むように作成または変更されます。

(conflict: ARRAY)

ARRAY ON [NAME] CONFLICT

(キーワードNAMEはオプションです。)

競合するフィールド名は、非フレックス列から読み取られます。競合がある各名前の値は非フレックス列で、要素が値である配列に変更されます。1つは非フレックス列で、もう1つはフレックス列データで、その順序で変更されます。

たとえば、特定のドキュメントについて、非フレックス・フィールドquantityの値が100で、フレックス列データに値が"314"のフィールドquantityがある場合、非フレックス・フィールドquantityの値は配列[100,314]に変更されます。

(conflict: IGNORE)

IGNORE ON [NAME] CONFLICT

(キーワードNAMEはオプションです。)

競合するフィールド名は、非フレックス列から読み取られます。フレックス列の同じ名前は無視されます。

(conflict: ERROR)

ERROR ON [NAME] CONFLICT

(キーワードNAMEはオプションです。)

エラーが発生します。

たとえば、このGraphQLフレックス宣言では、列extrasをフレックス列として定義し、そのフィールド名から発生する可能性のある競合が、単にフレックス列データの問題のあるフィールドを無視することによって処理されるように指定します:

extras: JSON @flex (conflict: IGNORE)

ノート:

IGNORE ON CONFLICTおよびARRAY ON CONFLICTは、ETAGチェックと互換性がありません。ETAGがチェックされ、これらの競合の宣言のいずれかがあるフレックス列を含む二面性ビューを作成しようとすると、エラーが発生します。