21.10 コンバータの使用(デフォルトの動作)

デフォルトの構成フィールド値(minFieldFrequencyおよびminTypeFrequencyを除く)を指定したJSON-to-Dualityコンバータの使用を示します。具体的には、構成フィールドuseFlexFieldstrueです。ドキュメント・セットをサポートするために必要なデータベース・オブジェクトが推測され、それらを構築するためのSQL DDLコードが生成されます。

「二面性への移行、簡易レシピ」のケースとは異なり、ここでは、minFieldFrequencyおよびminTypeFrequencyのゼロ以外(かつデフォルト以外)の値(それぞれ25および15)が与える影響を確認します。入力ドキュメント・セットは、生成される二面性ビューの名前(studentteacherおよびcourse)と同じ(student_tabteacher_tabおよびcourse_tab)です。

ここでも、構成フィールドuseFlexFieldsの値trueを使用します。つまり、二面性ビューの基礎となる表にフレックス列があることを意味します。これにより、単一のSQLスカラー・データ型に値が一貫して対応していない一部のスカラー・フィールドをビューでサポートできるようになります。

ノート:

フレックス列の詳細は、次を参照してください:

『JSONリレーショナル二面性開発者ガイド』の「フレックス列(基本より詳しい説明)」

ここに示す例のドキュメント・セットは非常に小規模です。外れ値(高エントロピ)フィールドの処理を示すために、ここに示す例では、デフォルト値5ではなく、マイグレータ構成フィールドminFieldFrequency (値25)およびminTypeFrequency (値15)に大きい値を使用します。

フィールドが特定のドキュメント・セットの出現の外れ値となるのは、ドキュメントのminFieldFrequencyパーセント未満でフィールドが出現する場合です。

フィールドが特定のドキュメント・セットの型の外れ値となるのは、ドキュメントのminTypeFrequencyパーセント未満で、その値のいずれかが特定の型で出現する場合です。

  • 出現の外れ値フィールド(まれに出現するフィールド)は、コンバータによって基礎となる列のいずれにもマップされません。コンバータがフレックス列を生成する(構成フィールドuseFlexFields = true (デフォルト値))場合、インポータは、マップされていないフィールドを二面性ビューの基礎となる表のフレックス列に格納します。フレックス列がない場合、インポータはインポート・エラー・ログにマップされていないフィールドをレポートし、そのフィールドは二面性ビューではサポートされません。

  • 型の外れ値フィールド(値が通常とは異なるまれな型であるフィールド)は、異なる方法で処理されます。インポートでは、まれな型の値をフィールドの想定される型に変換しようとします。失敗した変換はインポート・エラー・ログにレポートされ、そのフィールドは二面性ビューでは使用されません。

構成フィールドminFieldFrequencyminTypeFrequencyおよびuseFlexFieldsの詳細は、『JSONリレーショナル二面性開発者ガイド』の「マイグレータ・パラメータを指定するJSON構成フィールド」を参照してください。

例21-18 INFER_SCHEMAおよびGENERATE_SCHEMA (useFlexFields = true)

スキーマを推測して生成するためのここに示すコードは、「二面性への移行、簡易レシピ」の例「頻度しきい値がゼロのINFER_SCHEMAおよびGENERATE_SCHEMA: 外れ値なし」と同じですが、(1) infer_schemaの構成フィールド入力引数には、小規模なドキュメント・セットに適したminFieldFrequency (25)およびminTypeFrequency (15)の値が含まれています。useFlexFieldsの値がデフォルト値trueであるため、デフォルトのコンバータの動作がよくわかります。

その他の構成フィールドのデフォルトの動作については、『JSONリレーショナル二面性開発者ガイド』の「マイグレータ・パラメータを指定するJSON構成フィールド」を参照してください。

DECLARE
  er_schema   JSON;
  schema_sql  CLOB;
BEGIN
  er_schema :=
   DBMS_JSON_DUALITY.infer_schema(
     JSON('{"tableNames"        : [ "STUDENT_TAB",
                                    "TEACHER_TAB",
                                    "COURSE_TAB"],
            "viewNames"         : [ "STUDENT",
                                    "TEACHER",
                                    "COURSE" ],
            "minFieldFrequency" : 25,
            "minTypeFrequency"  : 15}'));
  schema_sql := DBMS_JSON_DUALITY.generate_schema(er_schema);
  EXECUTE IMMEDIATE schema_sql;
END;
/

次の例は、ファンクションDBMS_JSON_DUALITY.infer_schemaによって返されるJSONスキーマを示しています。

例21-19 二面性ビューのINFER_SCHEMAからのJSONスキーマ: デフォルトの動作

{"tables"        :
  [ {"title"        : "map_course_root_to_student_root",
     "dbObject"     : "map_course_root_to_student_root",
     "type"         : "object",
     "dbObjectType" : "table",
     "dbMapTable"   : true,
     "properties"   : {"map_course_id"  : {"sqlType"   : "varchar2",
                                           "maxLength" : 64,
                                           "nullable"  : false},
                       "map_student_id" : {"sqlType" : "number",
                                           "nullable" : false}},
                      "required"     : [ "map_course_id", "map_student_id" ],
                      "dbPrimaryKey" : [ "map_course_id",
                                         "map_student_id"],
                      "dbForeignKey" : [ {"map_course_id"  : {"dbObject" : "course_root",
                                                              "dbColumn" : "course_id"}},
                                         {"map_student_id" : {"dbObject" : "student_root",
                                                              "dbColumn" : "student_id"}} ]},
    {"title"        : "teacher_root",
     "dbObject"     : "teacher_root",
     "type"         : "object",
     "dbObjectType" : "table",
     "properties"   : {"_id"          : {"sqlType"   : "number", "nullable" : false},
                       "name"         : {"sqlType"   : "varchar2",
                                         "maxLength" : 64,
                                         "nullable"  : true,
                                         "unique"    : false},
                       "salary"       : {"sqlType"  : "number",
                                         "nullable" : true,
                                         "unique"   : false},
                       "department"   : {"sqlType"   : "varchar2",
                                         "maxLength" : 64,
                                         "nullable"  : true,
                                         "unique"    : false},
                       "phone_number" : {"sqlType"  : "json",
                                         "nullable" : true,
                                         "unique"   : false}},
     "required"     : [ "_id" ],
     "dbPrimaryKey" : [ "_id" ]},
    {"title"        : "course_root",
     "dbObject"     : "course_root",
     "type"         : "object",
     "dbObjectType" : "table",
     "properties"   : {"name"             : {"sqlType"   : "varchar2",
                                             "maxLength" : 64,
                                             "nullable"  : true,
                                             "unique"    : false},
                       "course_id"        : {"sqlType"   : "varchar2",
                                             "maxLength" : 64,
                                             "nullable"  : false},
                       "credit_hours"     : {"sqlType"  : "json",
                                             "nullable" : true,
                                             "unique"   : false},
                       "class_type"       : {"sqlType"   : "varchar2",
                                             "maxLength" : 64,
                                             "nullable"  : true,
                                             "unique"    : false},
                       "avg_grade"        : {"sqlType"  : "json",
                                             "nullable" : true,
                                             "unique"   : false},
                       "_id_teacher_root" : {"sqlType"  : "number",
                                             "nullable" : true,
                                             "unique"   : false}},
     "required"     : [ "course_id" ],
     "dbPrimaryKey" : [ "course_id" ],
     "dbForeignKey" : [ {"_id_teacher_root" : {"dbObject" : "teacher_root",
                                               "dbColumn" : "_id"}} ]},
    {"title"        : "student_root",
     "dbObject"     : "student_root",
     "type"         : "object",
     "dbObjectType" : "table",
     "properties"   : {"age"        : {"sqlType" : "number",
                                       "nullable" : true,
                                       "unique" : false},
                       "name"       : {"sqlType" : "varchar2",
                                       "maxLength" : 64,
                                       "nullable" : true,
                                       "unique" : false},
                       "advisor_id" : {"sqlType" : "number",
                                       "nullable" : true,
                                       "unique" : false},
                       "student_id" : {"sqlType" : "number",
                                       "nullable" : false},
                       "dorm_id"    : {"sqlType" : "number",
                                       "nullable" : true,
                                       "unique" : false}},
     "required"     : [ "student_id" ],
     "dbPrimaryKey" : [ "student_id" ],
     "dbForeignKey" : [ {"advisor_id" : {"dbObject" : "teacher_root",
                                         "dbColumn" : "_id"}},
                        {"dorm_id"    : {"dbObject" : "student_dormitory",
                                         "dbColumn" : "dorm_id"}} ]},
    {"title"        : "student_dormitory",
     "dbObject"     : "student_dormitory",
     "type"         : "object",
     "dbObjectType" : "table",
     "properties"   : { "dorm_id"   : {"sqlType" : "number",
                                       "nullable" : false},
                        "dorm_name" : {"sqlType" : "varchar2",
                                       "maxLength" : 64,
                                       "nullable" : true,
                                       "unique" : false}},
     "required"     : [ "dorm_id" ],
     "dbPrimaryKey" : [ "dorm_id" ]} ],
 "views"         : [ {"title"               : "STUDENT",
                      "dbObject"            : "STUDENT",
                      "dbObjectType"        : "dualityView",
                      "dbObjectProperties"  : [ "insert", "update", "delete", "check" ],
                      "dbMappedTableObject" : "student_root",
                      "type"                : "object",
                      "properties"          :
                        {"_id"          : {"type"              : "number",
                                           "dbAssigned"        : true,
                                           "dbFieldProperties" : [ "check" ],
                                           "dbObject"          : "student_root",
                                           "dbColumn"          : "student_id"},
                         "dbPrimaryKey" : [ "_id" ],
                         "age"          : {"type"              : [ "number",
                                                                   "null" ],
                                           "dbFieldProperties" : [ "update", "check" ],
                                           "dbObject"          : "student_root",
                                           "dbColumn"          : "age"},
                         "name"         : {"type"              : [ "string", "null" ],
                                           "maxLength"         : 64,
                                           "dbFieldProperties" : [ "update", "check" ],
                                           "dbObject"          : "student_root",
                                           "dbColumn"          : "name"},
                         "courses"      :
                           {"type"   : "array",
                            "items"  : {"type"                : "object",
                                        "dbMappedTableObject" : "course_root",
                                        "properties" :
                                          {"dbPrimaryKey"     : [ "ora$mapCourseId",
                                                                  "ora$mapStudentId" ],
                                           "ora$mapCourseId"  :
                                             {"type"              : "string",
                                              "maxLength"         : 64,
                                              "dbAssigned"        : true,
                                              "dbFieldProperties" : [ "check" ]},
                                           "ora$mapStudentId" :
                                              {"type" : "number",
                                               "dbAssigned" : true,
                                               "dbFieldProperties" : [ "check" ] },
                                           "name"             :
                                             {"type"              : [ "string",
                                                                      "null" ],
                                              "maxLength"         : 64,
                                              "dbFieldProperties" : [ "update", "check" ],
                                              "dbObject"          : "course_root",
                                              "dbColumn"          : "name"},
                                           "avgGrade"         :
                                             {"type"              : [ "number",
                                                                      "string",
                                                                      "null" ],
                                              "dbFieldProperties" : [ "update", "check" ],
                                              "dbObject"          : "course_root",
                                              "dbColumn"          : "avg_grade"},
                                           "courseNumber"     :
                                             {"type"              : "string",
                                              "maxLength"         : 64,
                                              "dbFieldProperties" : [ "check" ],
                                              "dbObject"          : "course_root",
                                              "dbColumn"          : "course_id"}},
                                        "required"   : [ "ora$mapCourseId",
                                                         "ora$mapStudentId",
                                                         "courseNumber" ]}},
                         "advisorId" : {"type"              : [ "number", "null" ],
                                        "dbFieldProperties" : [ "update", "check" ],
                                        "dbObject"          : "student_root",
                                        "dbColumn"          : "advisor_id"},
                         "dormitory" : {"type"                : "object",
                                        "dbMappedTableObject" : "student_dormitory",
                                        "properties" :
                                          {"dormId"   :
                                             {"type"              : "number",
                                              "dbFieldProperties" : [ "check" ],
                                              "dbObject"          : "student_dormitory",
                                              "dbColumn"          : "dorm_id"},
                                           "dormName" :
                                             {"type"              : [ "string", "null" ],
                                              "maxLength"         : 64,
                                              "dbFieldProperties" : [ "update", "check" ],
                                             "dbObject"           : "student_dormitory",
                                             "dbColumn"           : "dorm_name"}},
                                        "required"  : [ "dormId" ]},
                         "studentId" : {"dbFieldProperties" : [ "computed" ]}}},
                     {"title"               : "COURSE",
                      "dbObject"            : "COURSE",
                      "dbObjectType"        : "dualityView",
                      "dbObjectProperties"  : [ "insert", "update", "delete", "check" ],
                      "dbMappedTableObject" : "course_root",
                      "type"                : "object",
                      "properties" :
                        {"_id"         : { "type"              : "string",
                                           "maxLength"         : 64,
                                           "dbAssigned"        : true,
                                           "dbFieldProperties" : [ "check" ],
                                           "dbObject"          : "course_root",
                                           "dbColumn"          : "course_id"},
                         "dbPrimaryKey" : [ "_id" ],
                         "name"         : {"type"              : [ "string", "null" ],
                                           "maxLength"         : 64,
                                           "dbFieldProperties" : [ "update", "check" ],
                                           "dbObject"          : "course_root",
                                           "dbColumn"          : "name"},
                         "teacher"      :
                           {"type"                : "object",
                            "dbMappedTableObject" : "teacher_root",
                            "properties" :
                            {"name"      : {"type"              : [ "string", "null" ],
                                            "maxLength" : 64,
                                            "dbFieldProperties" : [ "update", "check" ],
                                            "dbObject"          : "teacher_root",
                                            "dbColumn"          : "name"},
                             "teacherId" : {"type"              : "number",
                                            "dbFieldProperties" : [ "check" ],
                                            "dbObject"          : "teacher_root",
                                            "dbColumn"          : "_id"}},
                            "required"   : [ "teacherId" ]},
                         "courseId"     : {"dbFieldProperties" : [ "computed" ]},
                         "students"     :
                           {"type" : "array",
                            "items" :
                              {"type" : "object",
                               "dbMappedTableObject" : "student_root",
                               "properties" :
                                 {"dbPrimaryKey"     : [ "ora$mapCourseId",
                                                         "ora$mapStudentId" ],
                                  "ora$mapCourseId"  : {"type"              : "string",
                                                        "maxLength"         : 64,
                                                        "dbAssigned"        : true,
                                                        "dbFieldProperties" : [ "check" ]},
                                  "ora$mapStudentId" : {"type"              : "number",
                                                        "dbAssigned"        : true,
                                                        "dbFieldProperties" : [ "check" ]},
                                  "name"             :
                                    {"type"              : [ "string", "null" ],
                                     "maxLength"         : 64,
                                     "dbFieldProperties" : [ "update", "check" ],
                                     "dbObject"          : "student_root",
                                     "dbColumn"          : "name"},
                                  "studentId"        : {"type"              : "number",
                                                        "dbFieldProperties" : [ "check" ],
                                                        "dbObject"          : "student_root",
                                                        "dbColumn"          : "student_id"}},
                               "required"   : [ "ora$mapCourseId",
                                                "ora$mapStudentId",
                                                "studentId" ]}},
                         "creditHours"  :
                           {"type"              : [ "number", "string", "null" ],
                            "dbFieldProperties" : [ "update", "check" ],
                            "dbObject"          : "course_root",
                            "dbColumn"          : "credit_hours"}}},
                     {"title"               : "TEACHER",
                      "dbObject"            : "TEACHER",
                      "dbObjectType"        : "dualityView",
                      "dbObjectProperties"  : [ "insert", "update", "delete", "check" ],
                      "dbMappedTableObject" : "teacher_root",
                      "type"                : "object",
                      "properties" :
                        {"_id"             : {"type"               : "number",
                                              "dbFieldProperties"  : [ "check" ],
                                              "dbObject"           : "teacher_root",
                                              "dbColumn"           : "_id"},
                         "name"            : {"type"              : [ "string", "null" ],
                                              "maxLength"         : 64,
                                              "dbFieldProperties" : [ "update", "check" ],
                                              "dbObject"          : "teacher_root",
                                              "dbColumn"          : "name"},
                         "salary"          : {"type"              : [ "number", "null" ],
                                              "dbFieldProperties" : [ "update", "check" ],
                                              "dbObject"          : "teacher_root",
                                              "dbColumn"          : "salary"},
                         "department"      : {"type"              : [ "string", "null" ],
                                              "maxLength"         : 64,
                                              "dbFieldProperties" : [ "update", "check" ],
                                              "dbObject"          : "teacher_root",
                                              "dbColumn"          : "department"},
                         "phoneNumber"     :
                           {"type"              : [ "string", "array", "null" ],
                            "dbFieldProperties" : [ "update", "check" ],
                            "dbObject"          : "teacher_root",
                            "dbColumn"          : "phone_number"},
                         "coursesTaught"  : 
                           {"type"  : "array",
                            "items" :
                              {"type"                : "object",
                               "dbMappedTableObject" : "course_root",
                               "properties" :
                                 {"name"     :  {"type"              : [ "string", "null" ],
                                                 "maxLength"         : 64,
                                                 "dbFieldProperties" : [ "update", "check" ],
                                                 "dbObject"          : "course_root",
                                                 "dbColumn"          : "name"},
                                  "courseId"  : {"type"              : "string",
                                                 "maxLength"         : 64,
                                                 "dbFieldProperties" : [ "check" ],
                                                 "dbObject"          : "course_root",
                                                 "dbColumn"          : "course_id"},
                                  "classType" : {"type"              : [ "string", "null" ],
                                                 "maxLength"         : 64,
                                                 "dbFieldProperties" : [ "update", "check" ],
                                                 "dbObject"          : "course_root",
                                                 "dbColumn"          : "class_type"}},
                               "required"   : [ "courseId" ]}},
                         "studentsAdvised" :
                           {"type"  : "array",
                            "items" :
                              {"type"                : "object",
                               "dbMappedTableObject" : "student_root",
                               "properties" :
                                 {"name"      : {"type"              : [ "string", "null" ],
                                                 "maxLength"         : 64,
                                                 "dbFieldProperties" : [ "update", "check" ],
                                                 "dbObject"          : "student_root",
                                                 "dbColumn"          : "name"},
                                  "dormId"    : {"type"              : [ "number", "null" ],
                                                 "dbFieldProperties" : [ "update", "check" ],
                                                 "dbObject"          : "student_root",
                                                 "dbColumn"          : "dorm_id"},
                                  "studentId" : {"type"              : "number",
                                                 "dbFieldProperties" : [ "check" ],
                                                 "dbObject"          : "student_root",
                                                 "dbColumn"          : "student_id"}},
                               "required"   : [ "studentId" ]}}}} ],
 "configOptions" : {"outputFormat"  : "executable",
                    "useFlexFields" : true}}

minFieldFrequencyおよびminTypeFrequencyがゼロの場合に推測されるスキーマ(「二面性への移行、簡易レシピ」の例「外れ値がない二面性ビューのINFER_SCHEMAからのJSONスキーマ」を参照)との違いは、次のとおりです:脚注1

  • 学生の表およびビューの場合、列およびフィールドageはnumber型です。

  • コースの表およびビューの場合、列notesおよびフィールドNotes存在しません

minFieldFrequencyおよびminTypeFrequencyゼロの場合に推測されるスキーマでは、notes列およびフィールドが存在し、age列はjson型で、ageフィールドはnumber-or-string型です。

そのため、DDLコードを生成して二面性ビューとその表を作成する前でも、infer_schemaの出力から、この2つの外れ値フィールドに対して想定される事柄の一部を確認できます。age = "Nineteen"の学生のドキュメントが存在することを思い出したなら、インポート時にそのドキュメントにはageフィールドがないことがすでにわかります。

次の例に、generate_schemaによって生成されたDDLコードを示します。

例21-20 GENERATE_SCHEMAからのDDLコード(useFlexFields = true)

ファンクションDBMS_JSON_DUALITY.generate_schemaは、ファンクションinfer_schema (「コンバータの使用(デフォルトの動作)」の例「INFER_SCHEMAおよびGENERATE_SCHEMA (useFlexFields = true)」)によって返される「コンバータの使用(デフォルトの動作)」の例「二面性ビューのINFER_SCHEMAからのJSONスキーマ: デフォルトの動作」のJSONスキーマが入力として渡された場合、ここに示す生成されたDDLコードを生成します。

「二面性への移行、簡易レシピ」の例「GENERATE_SCHEMAからのDDLコード(外れ値なしのユースケース)」との相違点は、次のとおりです:

  • ここでは、列student_root.ageの型はnumberで、number-or-stringではありません。

  • フィールドNotesをサポートするための列course_root.notesがありません。(かわりに、インポータはフィールドNotesをフレックス列course_root.ora$course_flexに格納します。)

ここでの二面性ビュー定義では、GraphQL構文を使用します。「コンバータの使用(デフォルトの動作)」の例「二面性ビュー作成用のSQL DDLコード(useFlexFields = true)」に、同等のSQL二面性ビュー定義を示します。

BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE student_dormitory(
   dorm_id  number  GENERATED BY DEFAULT ON NULL AS IDENTITY,
   dorm_name  varchar2(64),
   ora$student_flex  JSON(Object),
   PRIMARY KEY(dorm_id)
)';

EXECUTE IMMEDIATE 'CREATE TABLE map_course_root_to_student_root(
   map_course_id  varchar2(64)  DEFAULT ON NULL SYS_GUID(),
   map_student_id  number  GENERATED BY DEFAULT ON NULL AS IDENTITY,
   ora$student_flex  JSON(Object),
   ora$course_flex  JSON(Object),
   PRIMARY KEY(map_course_id,map_student_id)
)';

EXECUTE IMMEDIATE 'CREATE TABLE student_root(
   age  number,
   name  varchar2(64),
   dorm_id  number,
   advisor_id  number,
   student_id  number  GENERATED BY DEFAULT ON NULL AS IDENTITY,
   ora$student_flex  JSON(Object),
   ora$teacher_flex  JSON(Object),
   PRIMARY KEY(student_id)
)';

EXECUTE IMMEDIATE 'CREATE TABLE teacher_root(
   "_id"  number  GENERATED BY DEFAULT ON NULL AS IDENTITY,
   name  varchar2(64),
   salary  number,
   department  varchar2(64),
   phone_number  json VALIDATE ''{"oneOf" : [{ "type" :"string"}, { "type" :"array"}]}'',
   ora$teacher_flex  JSON(Object),
   ora$course_flex  JSON(Object),
   PRIMARY KEY("_id")
)';

EXECUTE IMMEDIATE 'CREATE TABLE course_root(
   name  varchar2(64),
   avg_grade  json VALIDATE ''{"oneOf" : [{ "type" :"number"}, { "type" :"string"}]}'',
   course_id  varchar2(64)  DEFAULT ON NULL SYS_GUID(),
   class_type  varchar2(64),
   credit_hours  json VALIDATE ''{"oneOf" : [{ "type" :"number"}, { "type" :"string"}]}'',
   "_id_teacher_root"  number,
   ora$teacher_flex  JSON(Object),
   ora$course_flex  JSON(Object),
   PRIMARY KEY(course_id)
)';

EXECUTE IMMEDIATE 'ALTER TABLE map_course_root_to_student_root
ADD CONSTRAINT fk_map_course_root_to_student_root_to_course_root
  FOREIGN KEY (map_course_id) REFERENCES course_root(course_id) DEFERRABLE';

EXECUTE IMMEDIATE 'ALTER TABLE map_course_root_to_student_root
ADD CONSTRAINT fk_map_course_root_to_student_root_to_student_root
  FOREIGN KEY (map_student_id) REFERENCES student_root(student_id) DEFERRABLE';

EXECUTE IMMEDIATE 'ALTER TABLE student_root
ADD CONSTRAINT fk_student_root_to_teacher_root
  FOREIGN KEY (advisor_id) REFERENCES teacher_root("_id") DEFERRABLE';

EXECUTE IMMEDIATE 'ALTER TABLE student_root
ADD CONSTRAINT fk_student_root_to_student_dormitory
  FOREIGN KEY (dorm_id) REFERENCES student_dormitory(dorm_id) DEFERRABLE';

EXECUTE IMMEDIATE 'ALTER TABLE course_root
ADD CONSTRAINT fk_course_root_to_teacher_root
  FOREIGN KEY ("_id_teacher_root") REFERENCES teacher_root("_id") DEFERRABLE';

EXECUTE IMMEDIATE 'CREATE INDEX IF NOT EXISTS
  fk_map_course_root_to_student_root_to_course_root_index
  ON map_course_root_to_student_root(map_course_id)';

EXECUTE IMMEDIATE 'CREATE INDEX IF NOT EXISTS
  fk_map_course_root_to_student_root_to_student_root_index
  ON map_course_root_to_student_root(map_student_id)';

EXECUTE IMMEDIATE 'CREATE INDEX IF NOT EXISTS
  fk_student_root_to_teacher_root_index
  ON student_root(advisor_id)';

EXECUTE IMMEDIATE 'CREATE INDEX IF NOT EXISTS
  fk_student_root_to_student_dormitory_index
  ON student_root(dorm_id)';

EXECUTE IMMEDIATE 'CREATE INDEX IF NOT EXISTS
  fk_course_root_to_teacher_root_index
  ON course_root("_id_teacher_root")';

EXECUTE IMMEDIATE 'CREATE OR REPLACE JSON RELATIONAL DUALITY VIEW STUDENT AS
student_root @insert @update @delete
{
  _id : student_id
  age
  name
  courses: map_course_root_to_student_root @insert @update @delete @array
  {
    ora$mapCourseId: map_course_id
    ora$mapStudentId: map_student_id
    ora$student_flex @flex
    course_root @unnest @insert @update @object
    {
      name
      avgGrade: avg_grade
      courseNumber: course_id
    }
  }
  advisorId:advisor_id
  dormitory: student_dormitory @insert @update @object
  {
    dormId: dorm_id
    dormName: dorm_name
    ora$student_flex @flex
  }
  studentId @generated (path: "$._id")
  ora$student_flex @flex
}';

EXECUTE IMMEDIATE 'CREATE OR REPLACE JSON RELATIONAL DUALITY VIEW TEACHER AS
teacher_root @insert @update @delete
{
  "_id"
  name
  salary
  department
  phoneNumber: phone_number
  coursesTaught: course_root @insert @update @delete @array
  {
    name
    courseId: course_id
    classType: class_type
    ora$teacher_flex @flex
  }
  studentsAdvised: student_root @insert @update @delete @array
  {
    name
    dormId:dorm_id
    studentId: student_id
    ora$teacher_flex @flex
  }
  ora$teacher_flex @flex
}';

EXECUTE IMMEDIATE 'CREATE OR REPLACE JSON RELATIONAL DUALITY VIEW COURSE AS
course_root @insert @update @delete
{
  _id : course_id
  name
  teacher: teacher_root @insert @update @object
  {
    name
    teacherId: "_id"
    ora$course_flex @flex
  }
  courseId @generated (path: "$._id")
  students: map_course_root_to_student_root @insert @update @delete @array
  {
    ora$mapCourseId: map_course_id
    ora$mapStudentId: map_student_id
    ora$course_flex @flex
    student_root @unnest @insert @update @object
    {
      name
      studentId: student_id
    }
  }
  creditHours: credit_hours
  ora$course_flex @flex
}';

EXECUTE IMMEDIATE 'CREATE OR REPLACE TRIGGER INSERT_TRIGGER_STUDENT
  BEFORE INSERT
  ON STUDENT
  FOR EACH ROW
DECLARE
  inp_jsonobj json_object_t;
BEGIN
  inp_jsonobj := json_object_t(:new.data);
  IF NOT inp_jsonobj.has(''_id'')
  THEN
    inp_jsonobj.put(''_id'', inp_jsonobj.get(''studentId''));
    :new.data := inp_jsonobj.to_json;
  END IF;
END;';

EXECUTE IMMEDIATE 'CREATE OR REPLACE TRIGGER INSERT_TRIGGER_COURSE
  BEFORE INSERT
  ON COURSE
  FOR EACH ROW
DECLARE
  inp_jsonobj json_object_t;
BEGIN
  inp_jsonobj := json_object_t(:new.data);
  IF NOT inp_jsonobj.has(''_id'')
  THEN
    inp_jsonobj.put(''_id'', inp_jsonobj.get(''courseId''));
    :new.data := inp_jsonobj.to_json;
  END IF;
END;';
END;

二面性ビューとその基礎となる表の作成に加えて、DDLコードはデフォルトの動作の一環として次を実行します:

  • 二面性ビュー<view-name>ごとに、サポートされているドキュメント内のオブジェクトの最上位フィールドの直下にある各表には、ora$<view-name>_flexという名前のフレックス列があります(これは、DDLの生成でuseFlexFieldsが暗黙的にtrueだったためです)。

  • student_rootおよびteacher_rootにはそれぞれ、主キー列student_idおよび_idがあります。

  • course_rootには、主キー列course_idがあります。列_id_teacher_rootは、表teacher_rootの主キーである列_idに対する外部キーです。表course_rootには、外部キー列_id_teacher_rootに対する索引があります。

  • map_course_root_to_student_rootは、表course_rootstudent_rootの間のマッピング表です。

    • 主キーは、列map_course_idおよびmap_student_idの複合です。

    • map_course_idおよびmap_student_idはそれぞれ、表course_rootおよびstudent_rootの主キー列である列course_idおよびstudent_idに対する外部キーです。

    • 2つの外部キー列に対する索引があります。

  • ビューcourseおよびstudentにはそれぞれ、値が格納されず、ビューのフィールド_idの値から生成されるフィールド(それぞれcourseIdおよびstudentId)があります。

    これは、二面性ビューには、その基礎となるルート表の識別列に対応する_idフィールドが必要ですが、既存のアプリケーションからのドキュメントには、かわりにcourseIdまたはstudentIdフィールドがあるためです。ビューcourseおよびstudentでは、これらのフィールドは常にフィールド_idから生成されるため、ドキュメントを挿入すると、かわりにフィールド_idに値が格納されます。(『JSONリレーショナル二面性開発者ガイド』の「二面性ビューのドキュメント識別子フィールド」を参照してください。)

  • ビューcourseおよびstudentにはそれぞれ、受信フィールドcourseIdまたはstudentIdの値をそれぞれ、フィールド_idに格納する挿入前トリガー(それぞれinsert_trigger_courseおよびinsert_trigger_student)があります。受信ドキュメントの最上位レベルにまだフィールド_idがない場合、トリガーは(1)それを追加し、(2)フィールドcourseIdまたはstudentIdの値を指定します。(インポートではINSERT操作が使用され、これらのトリガーはそのような操作の直前に起動します。)

例21-21 二面性ビュー作成用のSQL DDLコード(useFlexFields = true)

GraphQLよりもSQLに馴染みがある場合の参考までに、このSQL DDLコードは、前述の例「GENERATE_SCHEMAからのDDLコード(useFlexFields = true)」で示しているGraphQL二面性ビュー作成コードと同等です。

CREATE OR REPLACE JSON RELATIONAL DUALITY VIEW STUDENT AS
  SELECT JSON {'_id' : s.student_id,
               'age'       :  s.age,
               'name'      :  s.name,
               'courses'   :
                  [SELECT JSON {'ora$mapCourseId'  : m.map_course_id,
                                'ora$mapStudentId' : m.map_student_id,
                                 m.ora$student_flex AS FLEX,
                                 UNNEST
                                 (SELECT JSON {'name'         : c.name,
                                               'avgGrade'     : c.avg_grade,
                                               'courseNumber' : c.course_id}
                                    FROM course_root c WITH INSERT UPDATE
                                    WHERE c.course_id = m.map_course_id)}
                    FROM map_course_root_to_student_root m WITH INSERT UPDATE DELETE
                    WHERE s.student_id = m.map_student_id],
               'advisorId' :  s.advisor_id,
               'dormitory' : 
                  (SELECT JSON {'dormId'    : sd.dorm_id,
                                'dormName'  : sd.dorm_name,
                                 sd.ora$student_flex AS FLEX}
                      FROM student_dormitory sd  WITH INSERT UPDATE
                      WHERE s.dorm_id = sd.dorm_id),
               'studentId' IS GENERATED USING PATH '$._id',
               s.ora$student_flex AS FLEX 
               RETURNING JSON}
    FROM student_root s WITH INSERT UPDATE DELETE;

CREATE OR REPLACE JSON RELATIONAL DUALITY VIEW TEACHER AS
  SELECT JSON {'_id'           : t."_id",
               'name'          : t.name,
               'salary'        : t.salary,
               'department'    : t.department,
               'coursesTaught' :
                 [SELECT JSON {'name'      : c.name,
                               'courseId'  : c.course_id,
                               'classType' : c.class_type,
                               c.ora$teacher_flex AS FLEX}
                    FROM course_root c WITH INSERT UPDATE DELETE
                    WHERE c."_id_teacher_root" = t."_id"],
               'studentsAdvised' : 
                 [SELECT JSON {'name'      : s.name,
                               'dormId'    : s.dorm_id,
                               'studentId' : s.student_id,
                               s.ora$teacher_flex AS FLEX}
                    FROM student_root s WITH INSERT UPDATE DELETE
                    WHERE s.advisor_id = t."_id"],
               t.ora$teacher_flex AS FLEX
               RETURNING JSON}
    FROM teacher_root t WITH INSERT UPDATE DELETE;

CREATE OR REPLACE JSON RELATIONAL DUALITY VIEW COURSE AS
  SELECT JSON {'_id'        : c.course_id,
                'name'      : c.name,
                'teacher'   : 
                  (SELECT JSON {'name'      : t.name,
                                'teacherId' : t."_id",
                                t.ora$course_flex AS FLEX}
                     FROM teacher_root t WITH INSERT UPDATE
                WHERE t."_id" = c."_id_teacher_root"),
                'courseId' IS GENERATED USING PATH '$._id',
                'students' :
                   [SELECT JSON {'ora$mapCourseId'  : m.map_course_id,
                                 'ora$mapStudentId' : m.map_student_id,
                                 m.ora$course_flex AS FLEX,
                                 UNNEST
                                 (SELECT JSON {'name'      : s.name,
                                               'studentId' : s.student_id}
                                    FROM student_root s WITH INSERT UPDATE
                                    WHERE s.student_id = m.map_student_id)}
                     FROM map_course_root_to_student_root m WITH INSERT UPDATE DELETE
                     WHERE c.course_id = m.map_course_id],
                'creditHours' : c.credit_hours,
                c.ora$course_flex AS FLEX 
                RETURNING JSON}
    FROM course_root c WITH INSERT UPDATE DELETE;

DDLコードを生成してから実行すると、二面性ビューとその基礎となる表が作成されます。

DDLコードの実行後、入力表および二面性ビューの各種類(学生、教師、コース)に対してコンバータ・ファンクションDBMS_JSON_DUALITY.validate_schema_reportを実行して、変換を検証します。

例21-22 VALIDATE_SCHEMA_REPORT (デフォルト・ケース(useFlexFields = true))

SELECT * FROM DBMS_JSON_DUALITY.validate_schema_report(
                                  table_name => 'STUDENT_TAB',
                                  view_name  => 'STUDENT');
SELECT * FROM DBMS_JSON_DUALITY.validate_schema_report(
                                  table_name => 'TEACHER_TAB',
                                  view_name  => 'TEACHER');
SELECT * FROM DBMS_JSON_DUALITY.validate_schema_report(
                                  table_name => 'COURSE_TAB',
                                  view_name  => 'COURSE');

学生のデータの場合、学生Luis F.のドキュメント(studentId = 9)内のフィールドageの文字列値("Nineteen")に関するエラーがレポートされます。数値またはnull値しか使用できません。

ファンクションvalidate_schema_reportは、異常な入力ドキュメントをその出力の列DATAに格納し、同じレポート行の列ERRORSにエントリを格納します:

DATA
--------
{"studentId":9,"name":"Luis F.","age":"Nineteen","advisorId":101,"courses":[{"co
urseNumber":"CS101","name":"Algorithms","avgGrade":75},{"courseNumber":"MATH102"
,"name":"Calculus","avgGrade":95},{"courseNumber":"MATH103","name":"Advanced Alg
ebra","avgGrade":82}],"dormitory":{"dormId":201,"dormName":"ABC"}}
ERRORS
----------
[{"schemaPath":"$","instancePath":"$","code":"JZN-00501","error":"JSON schema va
lidation failed"},{"schemaPath":"$.properties","instancePath":"$","code":"JZN-00
514","error":"invalid properties: 'age'"},{"schemaPath":"$.properties.age.type",
"instancePath":"$.age","code":"JZN-00504","error":"invalid type found, actual: s
tring, expected one of: number, null"},{"schemaPath":"$.properties.age.extendedT
ype","instancePath":"$.age","code":"JZN-00504","error":"invalid type found, actu
al: string, expected one of: number, null"}]

そのドキュメントの場合、インポータは、入力データの文字列値"Nineteen"を数値に変換しようとして失敗します。その型の失敗をエラーとしてログに記録します。入力文字列値がかわりに"19"であった場合、インポータは値を数値19に変換し、そのように格納できます。

教師のデータに関するエラーはレポートされていません。

コースのデータに関するエラーもレポートされていません。具体的には、Algebraコース(MATH101)の出現の外れ値フィールドNotesに関するエラーはありません。これは、useFlexFields = trueによって、基礎となる表に対してフレックス列が作成されるためです。列にマップされていないすべての入力フィールドと同様に、インポータはフィールドNotesをフレックス列に格納するため、course二面性ビューでサポートされます。

この時点で、インポートの前に、Luis Jの学生の入力ドキュメントのageフィールドを変更して、文字列値"Nineteen"ではなく数値19を指定できます。問題のあるデータの修正に加えて、インポート前にDDLスクリプトを変更/編集し、変換動作や作成するビュー、表または索引の名前を変更することが必要になる場合があります。



脚注一覧

脚注1: これらのフィールドはすべてnull型でもあります。これは通常、識別列に対応しないフィールドに当てはまります。