7 生成フィールド、非表示フィールド
二面性ビューでは、JSONフィールドをリレーショナル列に直接マッピングするかわりに、SQL/JSONパス式、SQL式またはSQL問合せを使用してフィールドを生成できます。生成されたフィールド、および列にマップされたフィールドは非表示にできます。つまり、そのビューでサポートされているドキュメント内に表示されません。
生成フィールドの値の計算では、そのビューによって定義されている他のフィールドの値を使用できます(他の生成フィールドなど)。サポートされているドキュメント内でこれらのフィールドを非表示にするか表示するかは関係ありません。
式や問合せを使用してフィールド値を生成することは、インライン拡張とも呼ばれます。二面性ビューでサポートされているドキュメントが読み取られるときに、生成フィールドの追加によってそれが拡張されます。拡張の定義が二面性ビュー定義/作成コード(DDL)の一部であるという意味で、インラインと呼ばれます。
生成フィールドは読取り専用であり、ドキュメントが書き込まれるときには無視されます。それらに注釈(CHECK
など)を含めることはできません(それらは、フィールドetag
の値の計算には影響しません)。
ノート:
同じ列を様々な二面性ビュー内の複数のフィールドにマッピングすると、それらのサポート対象ドキュメントでは、それらのフィールド内でその同じデータが共有されます。生成フィールドの使用により、別の方法で様々な二面性ビューの間でデータを共有できます。あるビュー内のフィールドの値が別のビュー内のフィールドとまったく同じである必要がない場合に、その値が他のフィールドの値によって決まるようにすることができます。
ある種類のドキュメント内のフィールドの値を、宣言的に、他の種類のドキュメント(いくつでも可)のフィールドの値に応じて定義できます。生成フィールドは読取り専用であるため、この種の共有は一方向です。
これは二面性ビューで宣言的な代替手段を提供するための別の方法であり、ビジネス・ロジックを、アプリケーション・コードで実装するのではなく、アプリケーション・データ自体の定義に組み込むことができます。
たとえば、例7-2を参照してください。そこでは、チーム・ドキュメントのpoints
フィールドは、チームのドライバ用のドキュメントのpoints
フィールドによってのみ定まります。つまり、チームのポイントは、ドライバのポイントの合計です。
ノート:
非表示フィールドの名前が、同じ表のフレックス列に格納されているフィールドの名前と競合する場合、二面性ビューでサポートされるドキュメントでは、その表に対応するJSONオブジェクトからのフィールドは存在しません。SQLでは、生成フィールドを、そのフィールド名とコロン(:
)の直後にキーワードGENERATED
、その後にキーワードUSING
および次のいずれかを続けて指定します:
-
キーワード
PATH
の後にSQL/JSONパス式が続く -
SQL式
-
カッコで囲んだSQL問合せ:
(
…)
。
GraphQLでは、ディレクティブ@generated
を使用して生成フィールドを指定し、それに、引数path
またはsql
と、その値としてパス式(path
の場合)およびSQL式または問合せ(sql
の場合)を渡します。
パス式を指定した場合は、その式でターゲット指定(一致)したJSONデータを、その二面性ビューでサポートされているドキュメント内のどこにでも配置できます。つまり、パス式のスコープはドキュメント全体です。
具体的に述べると、パス式では、生成されるドキュメント・フィールドを参照できるということです。ビュー作成コードでパス式が字句的に出現する前にフィールドの生成が定義されている場合は、生成フィールドを使用してターゲット・データを特定することもできます。
パス式で他のフィールド値を使用して値が計算される場合(通常は計算される)、これらの計算で使用されるフィールドは非表示にできます。したがって、パス式では非表示フィールドを参照できます。つまり、パス式のスコープは、フィールドが非表示になる前の、生成されたドキュメントです。
SQL式または問合せを指定する場合は、それで、そのフィールドが属するJSONオブジェクトの基になる表の列(1)、(2)外部表の列、または(3)二面性ビューでサポートされているどのフィールドにもマップされていない列にあるSQLデータのみを参照する必要があります。
つまり、SQL式または問合せのスコープは、そのSQL式または問合せ自体、およびそれを字句的に含む問合せです。副問合せ内の表の列は参照可能ではありません。生成されるJSONデータに関しては、そのスコープは、生成フィールドが属するJSONオブジェクト、およびそのオブジェクトを含むJSONデータです。
たとえば、例7-1では、生成フィールドonPodium
は、表driver_race_map
(これは、フィールドonPodium
が属するJSONオブジェクトの基になる)の列position
を参照するSQL式を使用して定義されています。
1つ以上の式または問合せで非表示フィールドの値を使用して、他のフィールドの値を計算できます(それら自体はサポート対象ドキュメント内で非表示になっていても表示されていてもかまいません)。フィールドを非表示にすることを指定するには、それにマップされている列名の後にキーワードHIDDEN
を使用するか、そのフィールドを生成するGENERATED USING
句を使用します。
例7-1 SQL問合せおよびSQL式を使用して生成されるフィールド
この例では、二面性ビューrace_dv_sql_gen
を定義します。この定義は、例3-5でのビューrace_dv
の場合と同じですが、さらに次の2つの生成フィールドがあります:
-
fastestTime
— レースの最速時間。SQL問合せによるフィールド生成を使用します。 -
onPodium
— 指定されたドライバのレース結果でそのドライバが表彰台に上がるかどうか。SQL式によるフィールド生成を使用します。
fastestTime
の値は、表彰台に上がるドライバのレース時間にSQL集計関数min
を適用することで計算されます。これらは、レース表のJSON
型列podium
のオブジェクト・フィールドwinner
のフィールドtime
(podium.winner.time
)から取得されます。
onPodium
の値は、表driver_race_map
の列position
の値から計算されます。その列値が1
、2
または3
の場合、フィールドonPodium
の値は"YES"
であり、それ以外の場合は"NO"
です。このロジックは、SQLのCASE
式を評価することで実現されます。
GraphQL:
CREATE JSON RELATIONAL DUALITY VIEW race_dv_sql_gen AS
race
{_id : race_id
name : name
laps : laps @NOUPDATE
podium : podium @NOCHECK
fastestTime @generated (sql : "SELECT min(rt.podium.winner.time) FROM race rt")
result : driver_race_map @insert @update @delete @link (to : ["RACE_ID"])
{driverRaceMapId : driver_race_map_id
onPodium @generated (sql : "(CASE WHEN position BETWEEN 1 AND 3
THEN 'YES'
ELSE 'NO'
END)")
driver @unnest @update @noinsert @nodelete
{driverId : driver_id
name : name}}};
(この定義では、GraphQLディレクティブ@link
と引数to
を使用して、フィールドresult
の値であるネストされたオブジェクトに対して、表driver_race_map
の外部キー列race_id
を使用するように指定しています。これは、表race
の主キー列race_id
にリンクしています。「Oracle GraphQLディレクティブ@link」を参照してください。)
SQL:
CREATE JSON RELATIONAL DUALITY VIEW race_dv_sql_gen AS
SELECT JSON {'_id' : r.race_id,
'name' : r.name,
'laps' : r.laps WITH NOUPDATE,
'date' : r.race_date,
'podium' : r.podium WITH NOCHECK,
'fastestTime' : GENERATED USING
(SELECT min(rt.podium.winner.time) FROM race rt),
'result' :
[ SELECT JSON {'driverRaceMapId' : drm.driver_race_map_id,
'position' : drm.position,
'onPodium' : GENERATED USING
(CASE WHEN position BETWEEN 1 AND 3
THEN 'YES'
ELSE 'NO'
END),
UNNEST (SELECT JSON {'driverId' : d.driver_id,
'name' : d.name}
FROM driver d WITH NOINSERT UPDATE NODELETE
WHERE d.driver_id = drm.driver_id)}
FROM driver_race_map drm WITH INSERT UPDATE DELETE
WHERE drm.race_id = r.race_id ]}
FROM race r WITH INSERT UPDATE DELETE;
例7-2 SQL/JSONパス式を使用して生成されるフィールド
この例では、二面性ビューteam_dv_path_gen
を定義します。この定義は、チームのポイントがteam
表に格納されない点を除き、例3-1でのビューteam_dv
の場合と同じです。それらは、チーム内のドライバのポイントを合計することで計算されます。
SQL/JSONパス式$.driver.points.sum()
によってこれを実現します。これは、集計項目メソッドsum()
を表driver
の列points
内の値に適用します。
GraphQL:
CREATE JSON RELATIONAL DUALITY VIEW team_dv_path_gen AS
team @insert @update @delete
{_id : team_id
name : name
points @generated (path : "$.driver.points.sum()")
driver @insert @update @link (to : ["TEAM_ID"])
{driverId : driver_id
name : name
points : points @nocheck}};
SQL:
CREATE JSON RELATIONAL DUALITY VIEW team_dv_path_gen AS
SELECT JSON {'_id' : t.team_id,
'name' : t.name,
'points' : GENERATED USING PATH '$.driver.points.sum()',
'driver' :
[ SELECT JSON {'driverId' : d.driver_id,
'name' : d.name,
'points' : d.points WITH NOCHECK}
FROM driver d WITH INSERT UPDATE
WHERE d.team_id = t.team_id ]}
FROM team t WITH INSERT UPDATE DELETE;
以前は、このドキュメントでは、ドライバのpoints
フィールドとチームのpoints
フィールドの両方がアプリケーション・コードによって更新されることを想定していました。しかしながら、チームのpoints
は、ドライバのpoints
の値によってのみ定まります。このロジック(機能依存)をチームの二面性ビュー自体に統合し、それを宣言的に表すこと(チームのポイント = そのチームのドライバのポイントの合計)が理にかなっています。
例7-3 非表示フィールドを使用して生成されるフィールド
この例では、従業員表emp
を使用して二面性ビューemp_dv_gen
を定義します。
-
列
emp.wage
およびemp.tips
を使用して、非表示フィールドwage
およびtips
をそれぞれ定義します。 -
列
emp.wage
およびemp.tips
の値を合計するSQL式を使用して、フィールドtotalComp
を生成します。 -
フィールド
tips
およびwage
の値を比較するSQL/JSONパス式を使用して、ブール・フィールドhighTips
を生成します。
CREATE TABLE emp(empno NUMBER PRIMARY KEY,
first VARCHAR2(100),
last VARCHAR2(100),
wage NUMBER,
tips NUMBER);
INSERT INTO emp VALUES (1, 'Jane', 'Doe', 1000, 2000);
GraphQL:
CREATE JSON RELATIONAL DUALITY VIEW emp_dv_gen AS
emp
{_id : empno
wage : wage @hidden
tips : tips @hidden
totalComp @generated (sql : "wage + tips")
highTips @generated (path : "$.tips > $.wage")};
SQL:
CREATE JSON RELATIONAL DUALITY VIEW emp_dv_gen AS
SELECT JSON {'_id' : EMPNO,
'wage' : e.wage HIDDEN,
'tips' : e.tips HIDDEN,
'totalComp' : GENERATED USING (e.wage + e.tips),
'highTips' : GENERATED USING PATH '$.tips > $.wage'}
FROM emp e;
SELECT data FROM emp_dv_gen;
問合せ結果(わかりやすくするためフォーマット出力しています):
{"_id" : 1,
"totalComp" : 3000,
"highTips" : true,
"_metadata" : {"etag" : "B8CA77231CA578A6137788C83BC0F410",
"asof" : "000025B864BC59AB"}}
関連トピック