21.10 コンバータの使用(デフォルトの動作)
デフォルトの構成フィールド値(minFieldFrequency
およびminTypeFrequency
を除く)を指定したJSON-to-Dualityコンバータの使用を示します。具体的には、構成フィールドuseFlexFields
がtrue
です。ドキュメント・セットをサポートするために必要なデータベース・オブジェクトが推測され、それらを構築するためのSQL DDLコードが生成されます。
「二面性への移行、簡易レシピ」のケースとは異なり、ここでは、minFieldFrequency
およびminTypeFrequency
のゼロ以外(かつデフォルト以外)の値(それぞれ25および15)が与える影響を確認します。入力ドキュメント・セットは、生成される二面性ビューの名前(student
、teacher
およびcourse
)と同じ(student_tab
、teacher_tab
およびcourse_tab
)です。
ここでも、構成フィールドuseFlexFields
の値true
を使用します。つまり、二面性ビューの基礎となる表にフレックス列があることを意味します。これにより、単一のSQLスカラー・データ型に値が一貫して対応していない一部のスカラー・フィールドをビューでサポートできるようになります。
ここに示す例のドキュメント・セットは非常に小規模です。外れ値(高エントロピ)フィールドの処理を示すために、ここに示す例では、デフォルト値5
ではなく、マイグレータ構成フィールドminFieldFrequency
(値25
)およびminTypeFrequency
(値15
)に大きい値を使用します。
フィールドが特定のドキュメント・セットの出現の外れ値となるのは、ドキュメントのminFieldFrequency
パーセント未満でフィールドが出現する場合です。
フィールドが特定のドキュメント・セットの型の外れ値となるのは、ドキュメントのminTypeFrequency
パーセント未満で、その値のいずれかが特定の型で出現する場合です。
-
出現の外れ値フィールド(まれに出現するフィールド)は、コンバータによって基礎となる列のいずれにもマップされません。コンバータがフレックス列を生成する(構成フィールド
useFlexFields
=true
(デフォルト値))場合、インポータは、マップされていないフィールドを二面性ビューの基礎となる表のフレックス列に格納します。フレックス列がない場合、インポータはインポート・エラー・ログにマップされていないフィールドをレポートし、そのフィールドは二面性ビューではサポートされません。 -
型の外れ値フィールド(値が通常とは異なるまれな型であるフィールド)は、異なる方法で処理されます。インポートでは、まれな型の値をフィールドの想定される型に変換しようとします。失敗した変換はインポート・エラー・ログにレポートされ、そのフィールドは二面性ビューでは使用されません。
構成フィールドminFieldFrequency
、minTypeFrequency
および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_root
とstudent_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スクリプトを変更/編集し、変換動作や作成するビュー、表または索引の名前を変更することが必要になる場合があります。
-
サブプログラム
generate_schema、infer_schema
およびvalidate_import_report
の詳細は、『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』の「DBMS_JSON_DUALITY」を参照してください -
ファンクション
DBMS_JSON_SCHEMA.validate_report
の詳細は、『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』の「VALIDATE_REPORTファンクション」を参照してください
親トピック: JSONから二面性への移行
脚注一覧
脚注1: これらのフィールドはすべてnull型でもあります。これは通常、識別列に対応しないフィールドに当てはまります。