ヘッダーをスキップ
Oracle® Databaseオブジェクト・リレーショナル開発者ガイド
11gリリース2 (11.2)
E94920-01
  目次へ移動
目次
索引へ移動
索引

前
 
次
 

6 オブジェクト・モデルのリレーショナル・データへの適用

この章では、基礎となるリレーショナル・データの構造を変更することなく、オブジェクト指向アプリケーションを作成する方法を示します。

内容は次のとおりです。

オブジェクト・ビュー使用の利点

ビューが仮想表であることと同様、オブジェクト・ビューは仮想的なオブジェクト表です。ビューの各行はオブジェクトであるため、メソッドのコール、ドット表記法を使用したオブジェクト属性へのアクセスおよびオブジェクトを指すREFの作成が可能です。

オブジェクト指向アプリケーションは、既存の表を別の物理構造に変換せずに実行できます。このためには、オブジェクト・ビューを使用して、オブジェクト指向アプリケーションのプロトタイプを作成したり、オブジェクト指向アプリケーションに切り替えることができます。ビューの中のデータはリレーショナル表から取り出すことができ、オブジェクト表として定義されているかのようにアクセスできるためです。

オブジェクト・ビューは、リレーショナル・ビューのように使用し、目的のデータのみ画面上に表示できます。たとえば、給料など機密データを除いて、従業員表の特定のデータを表示するオブジェクト・ビューが作成できます。

オブジェクト・ビューの使用によって、パフォーマンスが向上します。オブジェクト・ビューの1行を構成するリレーショナル・データは、1単位としてネットワークを横断するため、ラウンドトリップが大幅に削減されます。

リレーショナル・データは、クライアント側のオブジェクト・キャッシュにフェッチでき、Cの構造体、C++またはJavaクラスにマップすることもできるので、3GLアプリケーションで固有のクラスのように操作できます。また、複雑なオブジェクト検索のようなオブジェクト指向機能をリレーショナル・データとともに使用することもできます。

  • リレーショナル・データからオブジェクトを合成することによって、新しい方法でデータを問い合せることができます。複数の表との複雑な結合を記述するかわりに、オブジェクトの参照解除を利用することで複数の表からデータを表示できます。

  • オブジェクト・ビューからオブジェクト・データを確保し、クライアント側のオブジェクト・キャッシュ内でそのデータを使用できます。オブジェクト・キャッシュ上に確保された、これらの合成されたオブジェクトを、専用のオブジェクト検索メカニズムにより取り出すことで、ネットワーク通信量を削減できます。

  • ビュー内部にオブジェクト・モデルを作成しながら、モデル開発が継続できるという、優れた柔軟性が得られます。オブジェクト型を変更する必要がある場合、無効なビューを新しい定義に簡単に置換できます。

  • ビューの中のオブジェクトを使用することで、基礎となる記憶域メカニズムの特性が制限されることはありません。同様に、現行のテクノロジによって制限されることもありません。たとえば、パラレル化およびパーティション化されたリレーショナル表からオブジェクトを合成することもできます。

  • 基礎となる同じデータから異なる複合データ・モデルを作成できます。


    関連項目:

    • SQL構文および使用方法の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

    • PL/SQLの機能の詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。

    • Javaの詳細は、『Oracle Database Java開発者ガイド』を参照してください。

    • これらの機能の詳細は、『Oracle Call Interfaceプログラマーズ・ガイド』を参照してください。


オブジェクト・ビューの定義

オブジェクト・ビューを定義する手順は、次のとおりです。

  1. 既存のリレーショナル表の列にそれぞれ対応するデータ型の属性を持つオブジェクト型を定義します。

  2. リレーショナル表からデータを抽出する方法を指定する問合せを作成します。オブジェクト型の属性と同じ順序で、リレーショナル表の列を指定します。

  3. 基礎となるデータの属性に基づいて、一意の値を指定します。これは、ビュー内のオブジェクトを指すポインタ(REF)を作成するためのオブジェクト識別子となります。既存の主キーを使用することもできます。

オブジェクト型の属性が既存の表の列に正確に対応していないオブジェクト・ビューを更新するには、次の手順を実行する必要があることがあります。

アプリケーション・プログラムがオブジェクト・ビューのデータを更新するときに、Oracleに実行させるINSTEAD OFトリガー・プロシージャを作成します。「オブジェクト・ビューの更新」を参照してください。

これで、オブジェクト・ビューをオブジェクト表と同様に使用できます。

例6-1のSQL文は、各行がemployee_t型のオブジェクトであるオブジェクト・ビューを定義しています。

例6-1 オブジェクト・ビューの作成

CREATE TABLE emp_table (
    empnum   NUMBER (5),
    ename    VARCHAR2 (20),
    salary   NUMBER (9,2),
    job      VARCHAR2 (20));

CREATE TYPE employee_t AS OBJECT (
    empno    NUMBER (5),
    ename    VARCHAR2 (20),
    salary   NUMBER (9,2),
    job      VARCHAR2 (20));
/

CREATE VIEW emp_view1 OF employee_t
    WITH OBJECT IDENTIFIER (empno) AS
        SELECT e.empnum, e.ename, e.salary, e.job
            FROM emp_table e
            WHERE job = 'Developer';

insert into emp_table values(1,'John',1000.00,'Architect');
insert into emp_table values(2,'Robert',900.00,'Developer');
insert into emp_table values(3,'James',2000.00,'Director');

select * from emp_view1;


    EMPNO ENAME                    SALARY JOB
---------- -------------------- ---------- --------------------
         2 Robert                      900 Developer

リレーショナル表のempnum列のデータにアクセスするには、オブジェクト型のempno属性にアクセスします。

アプリケーションにおけるオブジェクト・ビューの使用

オブジェクト・ビューの行にあるデータが複数の表から取り出される可能性もありますが、その場合もオブジェクト・ビューは1つの操作のみでネットワークを横断します。クライアント側のオブジェクト・キャッシュの中では、インスタンスはCまたはC++の構造体として、またはPL/SQLオブジェクト変数として現れます。このインスタンスを、他のすべてのシステム固有の構造体と同様に操作することもできます。

SQL文の中で、オブジェクト・ビューはオブジェクト表と同じ方法で参照できます。たとえば、オブジェクト・ビューが現れる可能性があるのは、SELECT構文のリスト、UPDATE-SET句またはWHERE句の中です。

また、オブジェクト・ビューにオブジェクト・ビューを定義することもできます。

オブジェクト表からオブジェクトに対して使用するものと同じOCIコールを使用して、クライアント側のオブジェクト・ビューのデータにアクセスできます。たとえば、REF確保するためにOCIObjectPin()を、また、オブジェクトをサーバーにフラッシュするためにOCIObjectFlush()を使用できます。オブジェクト・ビューのオブジェクトをデータベースに更新またはフラッシュすると、データベースでオブジェクト・ビューが更新されます。


関連項目:

OCIコールの詳細は、『Oracle Call Interfaceプログラマーズ・ガイド』を参照してください。

オブジェクト・ビューにおけるオブジェクトのネスト

オブジェクト型は、他のオブジェクト型を属性として自身にネストさせることができます。

オブジェクト・ビューの基になっているオブジェクト型が、それ自身がオブジェクト型である属性を持っている場合は、オブジェクト・ビューを作成する処理の一部として、この属性用の列オブジェクトを用意する必要があります。属性型の列オブジェクトがリレーショナル表にすでに存在する場合は、この列オブジェクトを選択するのみですみます。存在しない場合は、ビューの主オブジェクト・インスタンスを合成する場合と同様に、基礎となるリレーショナル・データからオブジェクト・インスタンスを合成する必要があります。これらのオブジェクトを合成または作成するには、オブジェクト型の各コンストラクタ・メソッドをコールしてオブジェクト・インスタンスを作成しますが、それらの属性には、コンストラクタで指定したリレーショナル列からデータを移入できます。

たとえば、例6-2の部門表deptについて考えてみます。住所が部門オブジェクト内部のオブジェクトになっているオブジェクト・ビューを作成します。これにより、住所オブジェクト用の再利用可能なメソッドを定義し、様々なアドレスに使用できるようになります。

まず住所オブジェクト用と部門オブジェクト用の型を作成してから、部門番号、名前および住所を含むビューを作成します。リレーショナル表の列から、addressオブジェクトが作成されます。

例6-2 ネストしたオブジェクト型を持つビューの作成

CREATE TABLE dept (
    deptno       NUMBER PRIMARY KEY,
    deptname     VARCHAR2(20),
    deptstreet   VARCHAR2(20),
    deptcity     VARCHAR2(10),
    deptstate    CHAR(2),
    deptzip      VARCHAR2(10));

CREATE TYPE address_t AS OBJECT (
   street   VARCHAR2(20),
    city    VARCHAR2(10),
    state   CHAR(2),
    zip     VARCHAR2(10));
/
CREATE TYPE dept_t AS OBJECT (
   deptno     NUMBER,
   deptname   VARCHAR2(20),
   address    address_t );
/

CREATE VIEW dept_view OF dept_t WITH OBJECT IDENTIFIER (deptno) AS
    SELECT d.deptno, d.deptname,
      address_t(d.deptstreet,d.deptcity,d.deptstate,d.deptzip) AS 
      deptaddr
      FROM dept d;

insert into dept values(1,'Sales','500 Oracle pkwy','Redwood S','CA','94065');
insert into dept values(2,'ST','400 Oracle Pkwy','Redwood S','CA','94065');
insert into dept values(3,'Apps','300 Oracle pkwy','Redwood S','CA','94065');

select * from dept_view;
  
    DEPTNO DEPTNAME
---------- --------------------
ADDRESS(STREET, CITY, STATE, ZIP)
----------------------------------------------------------------------------------
         1 Sales
ADDRESS_T('500 Oracle pkwy', 'Redwood S', 'CA', '94065')
 
         2 ST
ADDRESS_T('400 Oracle Pkwy', 'Redwood S', 'CA', '94065')
 
         3 Apps
ADDRESS_T('300 Oracle pkwy', 'Redwood S', 'CA', '94065')

オブジェクト・ビューにおけるNULLオブジェクトの識別

オブジェクトのコンストラクタからNULLが戻されることはありません。したがって、前述のビューにおいて、リレーショナル表の中のcity、streetなどの列がすべてNULLであっても、アドレス・オブジェクトがNULLになることはありません。リレーショナル表には、部門の住所がNULLかどうかを指定する列はありません。

DECODEファンクションまたは他のファンクションを使用して、NULLまたは構成されたオブジェクトのいずれかを返すことにより、NULLのdeptstreet列によって住所全体がNULLであることを表すことができます。

例6-3 オブジェクト・ビューにおけるNULLオブジェクトの識別

-- Requires Ex. 6-2
CREATE OR REPLACE VIEW dept_view AS
  SELECT d.deptno, d.deptname,
        DECODE(d.deptstreet, NULL, NULL, 
        address_t(d.deptstreet, d.deptcity, d.deptstate, d.deptzip)) AS deptaddr
  FROM dept d;

部門の住所はリレーショナル表の中の列と直接対応していないため、この方法を使用すると、ビューを介して部門の住所を直接更新できなくなります。この方法ではなく、ビュー上にINSTEAD OFトリガーを定義して、この列の更新を操作します。

オブジェクト・ビューにおけるネストした表およびVARRAYの使用

ネストした表およびVARRAYのコレクションはどちらも、ビューの列になる場合があります。これらのコレクションは、基礎となるコレクション列から選択するか、または副問合せを使用して合成できます。CAST-MULTISET演算子によって、これらのコレクションを合成します。

この項の内容は次のとおりです。

オブジェクト・ビューのシングルレベル・コレクション

最初に例6-1および例6-2を使用することで、empリレーショナル表に存在する各従業員は例6-4の構造を持ちます。このリレーショナル表を使用すると、部門番号、名前、住所および部門に所属する従業員のコレクションを持つdept_viewを構成できます。

まず、従業員型employee_t用のネストした表型を定義します。次に、部門番号、名前、住所、および従業員のネストした表を持つ部門型を定義します。最後に、オブジェクト・ビューdept_viewを定義します。

例6-4 シングルレベル・コレクションを持つビューの作成

-- Requires Ex. 6-1 and Ex. 6-2
CREATE TABLE emp (
   empno    NUMBER PRIMARY KEY,
   empname  VARCHAR2(20),
   salary   NUMBER,
   job      VARCHAR2 (20), 
   deptno   NUMBER REFERENCES dept(deptno));

CREATE TYPE employee_list_t AS TABLE OF employee_t;  -- nested table
/
CREATE TYPE dept_t AS OBJECT (
    deptno     NUMBER,
    deptname   VARCHAR2(20),
    address    address_t,
    emp_list   employee_list_t);
/
CREATE VIEW dept_view OF dept_t WITH OBJECT IDENTIFIER (deptno) AS
    SELECT d.deptno, d.deptname,
     address_t(d.deptstreet,d.deptcity,d.deptstate,d.deptzip) AS deptaddr,
             CAST( MULTISET (
                           SELECT e.empno, e.empname, e.salary, e.job
                           FROM emp e 
                           WHERE e.deptno = d.deptno) 
                        AS employee_list_t)
                   AS emp_list
   FROM dept d;

insert into dept values(100,'ST','400 Oracle Pkwy','Redwood S','CA',94065);
insert into dept values(200,'Sales','500 Oracle Pkwy','Redwood S','CA',94065);
insert into emp values(1,'John',900,'Developer1',100);
 
insert into emp values(2,'Robert',1000,'Developer2',100);
insert into emp values(3,'Mary', 1000,'Apps1',200);
insert into emp values(4,'Maria',1500,'Developer3',200);
select * from dept_view where deptno = 100;
 
    DEPTNO DEPTNAME
---------- --------------------
ADDRESS(STREET, CITY, STATE, ZIP)
--------------------------------------------------------------------------------
EMP_LIST(EMPNO, ENAME, SALARY, JOB)
--------------------------------------------------------------------------------
       100 ST
ADDRESS_T('400 Oracle Pkwy', 'Redwood S', 'CA', '94065')
EMPLOYEE_LIST_T(EMPLOYEE_T(1, 'John', 900, 'Developer1'), EMPLOYEE_T(2, 'Robert'
, 1000, 'Developer2'))
 
 
select emp_list from dept_view where deptno = 100;
 
EMP_LIST(EMPNO, ENAME, SALARY, JOB)
--------------------------------------------------------------------------------
EMPLOYEE_LIST_T(EMPLOYEE_T(1, 'John', 900, 'Developer1'), EMPLOYEE_T(2, 'Robert'
, 1000, 'Developer2'))

CAST-MULTISETブロック内のSELECT副問合せにより、現在の部門に所属する従業員のリストが選択されます。MULTISETキーワードは、これが単一の値とは対照的なリストであることを示します。CAST演算子は、結果セットを適切な型にキャスト(型変換)します。この場合は、ネストした表型employee_list_tにキャストします。

このビューへの問合せでは、それぞれの部門行に、部門番号、名前、住所オブジェクトおよびその部門に所属する従業員のコレクションが入っている部門リストが取得できます。

オブジェクト・ビューのマルチレベル・コレクション

マルチレベル・コレクションもシングルレベル・コレクションも、オブジェクト・ビューで同じ方法で作成し、使用します。異なるのは、マルチレベル・コレクションで、別のレベルのコレクションを作成する必要がある点のみです。

例6-5では、マルチレベル・コレクションが含まれているオブジェクト・ビューを作成します。このビューは、コレクションが何も入っていないフラットなリレーショナル表に基づいています。オブジェクト・ビューを作成する準備作業として、この例で使用するオブジェクト型とコレクション型を作成します。それぞれのリレーショナル表と対応するように、オブジェクト型(たとえば、emp_t)を定義し、その属性の型は、それぞれの表列の型と対応させます。また、従業員型はプロジェクトのネストした表(属性)を持ち、部門型は従業員のネストした表(属性)を持ちます。後者のネストした表は、マルチレベル・コレクションです。CREATE VIEW文の中でCAST-MULTISET演算子を使用して、コレクションを作成します。

例6-5 マルチレベル・コレクションを持つビューの作成

CREATE TABLE depts
  ( deptno     NUMBER,
    deptname   VARCHAR2(20));

CREATE TABLE emps
  ( ename VARCHAR2(20),
    salary     NUMBER,
    deptname   VARCHAR2(20));

CREATE TABLE projects
  ( projname   VARCHAR2(20),
    mgr        VARCHAR2(20));

CREATE TYPE project_t AS OBJECT
  ( projname   VARCHAR2(20),
    mgr        VARCHAR2(20));
/
CREATE TYPE nt_project_t AS TABLE OF project_t;
/
CREATE TYPE emp_t AS OBJECT
(  ename      VARCHAR2(20),
   salary     NUMBER,
   deptname   VARCHAR2(20),
   projects   nt_project_t );
/
CREATE TYPE nt_emp_t AS TABLE OF emp_t;
/
CREATE TYPE depts_t AS OBJECT
  ( deptno     NUMBER,
    deptname   VARCHAR2(20),
    emps       nt_emp_t );
/
CREATE VIEW v_depts OF depts_t WITH OBJECT IDENTIFIER (deptno) AS
  SELECT d.deptno, d.deptname, 
    CAST(MULTISET(SELECT e.ename, e.salary, e.deptname,
        CAST(MULTISET(SELECT p.projname, p.mgr
          FROM projects p
          WHERE p.mgr = e.ename)
        AS nt_project_t)
      FROM emps e
      WHERE e.deptname = d.deptname)
    AS nt_emp_t)
  FROM depts d;

オブジェクト・ビューに対するオブジェクト識別子の指定

オブジェクト・ビューの中で、行オブジェクトを指すポインタ(REF)を作成できます。ビューのデータは永続的に格納されるわけではないため、オブジェクト識別子として使用される個々の値を指定する必要があります。オブジェクト識別子を使用すると、オブジェクト・ビューにおけるオブジェクトを、オブジェクト・キャッシュ上で参照し、確保することができます。

このビューがオブジェクト表またはオブジェクト・ビューに基づいている場合には、すでに各行に対応付けられたオブジェクト識別子があり、これを再利用できます。このためには、WITH OBJECT IDENTIFIER句を省略するか、またはWITH OBJECT IDENTIFIER DEFAULT句を指定します。

ただし、行オブジェクトをリレーショナル・データから合成する場合は、他の値セットを選択する必要があります。

主キーに基づいたオブジェクト識別子を指定できます。この場合、行オブジェクトを識別する一意のキーの集合が、そのオブジェクトの識別子になります。値が重複すると、オブジェクト参照時に問題が発生することもあるため、これらの値はビューから選択された行内で一意である必要があります。


関連項目:

主キー・ベースのオブジェクト識別子とシステム生成のオブジェクト識別子の詳細は、「オブジェクト識別子による行オブジェクトの識別」を参照してください。

  • WITH OBJECT IDENTIFIER句を使用して作成されたオブジェクト・ビュー

    WITH OBJECT IDENTIFIER句を使用して作成されたオブジェクト・ビューは、主キーから導出されたオブジェクト識別子を持ちます。

    たとえば、「オブジェクト・ビューのシングルレベル・コレクション」で説明したオブジェクト型dept_tおよびオブジェクト・ビューdept_viewの定義について考えます。

    基礎となるリレーショナル表にdeptnoが主キーとして入っているため、各部門行は一意の部門番号を持ちます。このビューでは、deptno列がオブジェクト型のdeptno属性になります。ビュー・オブジェクト内でdeptnoが一意であることがわかれば、これをオブジェクト識別子として指定できます。

  • WITH OBJECT IDENTIFIER DEFAULT句を使用して作成されたオブジェクト・ビュー

    WITH OBJECT IDENTIFIER DEFAULT句を指定した場合、オブジェクト識別子は、基礎となる表またはビュー定義に応じてはシステム生成または主キー・ベースになります。


関連項目:

「オブジェクト識別子(OID)の記憶域上の考慮点」を参照してください。

オブジェクトを表示するための参照の作成

この例6-2例6-4の連結された例グループにおいて、dept_viewビューから選択された各オブジェクトは、部門番号の値から導出された一意のオブジェクト識別子を持ちます。リレーショナルの場合、従業員表empの外部キーdeptnoは、部門表deptの主キー値deptnoと一致します。主キー値はdept_view内にオブジェクト識別子を作成するため、emp_viewの外部キー値はdept_viewの主キー値の参照を作成できます。

主キーのオブジェクト参照を合成するには、MAKE_REF演算子を使用します。この演算子は、参照がポイントするビューまたは表の名前および外部キー値のリストをとり、参照されたビューの中の特定のオブジェクトと一致するような参照のオブジェクト識別子部分を作成します。

例6-6では、まず従業員型emp_tを作成し、次にその型に基づいたビューを作成することにより、従業員の番号、名前、給料、および所属部門の参照を持つemp_viewビューを作成します。

例6-6 ビューでのオブジェクトへの参照の作成

-- Requires Ex. 6-2 and Ex. 6-4
-- if you have previously created emp_t, you must drop it
CREATE TYPE emp_t AS OBJECT (
  empno    NUMBER,
  ename    VARCHAR2(20),
  salary   NUMBER,
  deptref  REF dept_t);
/
CREATE OR REPLACE VIEW emp_view OF emp_t WITH OBJECT IDENTIFIER(empno)
   AS SELECT e.empno, e.empname, e.salary, 
                      MAKE_REF(dept_view, e.deptno) 
         FROM emp e;

ビューの中のdeptref列に、部門の参照が格納されます。次の簡単な問合せでは、Redwood S市にある部門のすべての従業員を取り出します。

SELECT e.empno, e.salary, e.deptref.deptno
  FROM emp_view e
 WHERE e.deptref.address.city = 'Redwood S';

   EMPNO     SALARY DEPTREF.DEPTNO
---------- ---------- --------------
         2       1000            100
         1        900            100
         4       1500            200
         3       1000            200

次のようにMAKE_REFのかわりにREF修飾子を使用してemp_viewを作成しても、dept_viewオブジェクトの参照が取得できる点に注意してください。

例6-7 REFを使用したオブジェクトの問合せ参照

-- Requires Ex. 6-2, Ex. 6-4, and Ex. 6-6
CREATE OR REPLACE VIEW emp_view OF emp_t WITH OBJECT IDENTIFIER(empno)
   AS SELECT e.empno, e.empname, e.salary, REF(d)
         FROM emp e, dept_view d
          WHERE e.deptno = d.deptno;

例6-7では、dept_viewemp表はdeptnoキーで結合されます。

例6-6のようにREF修飾子のかわりにMAKE_REF演算子を使用すると、循環参照を作成できるというメリットがあります。たとえば、従業員の部門への参照を持つ従業員のビューを作成する一方で、その部門に所属している従業員への参照のリストを持つ部門のビューを作成することができます。

オブジェクト・ビューが主キー・ベースのオブジェクト識別子を持つ場合、このようなビューへの参照は主キー・ベースになることに注意してください。一方、システム生成のオブジェクト識別子を持つビューへの参照は、システム生成のオブジェクト参照になります。この違いが関係するのは、OCIオブジェクト・キャッシュでオブジェクト・インスタンスを作成し、新しく作成したオブジェクトの参照を取得する必要がある場合のみです。

合成オブジェクトの場合と同様、永続的に格納される参照をビュー列として選択し、これらを問合せで透過的に使用できます。ただし、ビュー・オブジェクトに対するオブジェクト参照を永続的に格納することはできません。

オブジェクト・ビューを利用した逆リレーションシップのモデル化

オブジェクトを持つビューを使用して、逆リレーションシップをモデル化できます。

1対1リレーションシップ

1対1リレーションシップは、オブジェクトの逆参照を使用してモデル化できます。たとえば、各従業員が専用のデスクトップ・コンピュータを持っているとしましょう。リレーショナル・モデルであれば、コンピュータ表から従業員表へ、または逆の方向で外部キーを使用して、1対1リレーションシップを獲得します。ビューを使用すると、従業員からコンピュータ・オブジェクトへのオブジェクト参照や、コンピュータ・オブジェクトから従業員の参照が行えるように、オブジェクトをモデル化できます。

1対多および多対1リレーションシップ

1対多リレーションシップ(または多対1リレーションシップ)は、オブジェクト参照を使用するか、オブジェクトを埋め込むことでモデル化できます。1対多リレーションシップは、オブジェクトのコレクションまたはオブジェクト参照のコレクションを持つことでモデル化できます。多対1リレーションシップは、オブジェクト参照を使用してモデル化できます。

部門対従業員のケースを検討してみましょう。基礎となるリレーショナル・モデルでは、外部キーが従業員表の中にあります。部門と従業員のリレーションシップは、ビュー内のコレクションを使用してモデル化できます。部門ビューに従業員のコレクションを持たせ、従業員ビューに部門の参照を持たせることができます(または部門値がインライン化できます)。これにより、前方リレーションシップ(従業員から部門へ)と逆リレーションシップ(部門から従業員リストへ)の両方が得られます。また、部門ビューに、従業員オブジェクトを埋め込むかわりに、従業員オブジェクトの参照のコレクションを持たせることも可能です。

オブジェクト・ビューの更新

オブジェクト表に使用するSQL DMLと同じものを使用して、オブジェクト・ビューのデータを更新、挿入および削除できます。あいまいな表現がなければ、オブジェクト・ビューの実表が更新されます。

ビューは常に直接更新できるわけではありません。

ビューの問合せに結合、集合演算子、集計ファンクション、GROUP BYまたはDISTINCT句が含まれている場合、ビューの直接更新はできません。ビューの問合せで、ビューの列が疑似列または式に基づいている場合も、ビューの個々の列を直接更新することはできません。

ビューが直接更新できなくても、INSTEAD OFトリガーを使用して、間接的に更新できます。この操作を行うには、ビューに対して実行するそれぞれのDML文に、INSTEAD OFトリガーを定義します。INSTEAD OFトリガーには、目的の変更をビューで行うために、ビューの基礎となる表に対して実行する必要のある操作をプログラムします。この後、INSTEAD OFトリガーが定義されたDML文を発行すると、関連するトリガーが透過的に実行されます。


関連項目:

INSTEAD OFトリガーの例は、「変更および妥当性チェックを制御するINSTEAD OFトリガーの使用」を参照してください。


注意:

オブジェクト・ビュー階層では、UPDATE文およびDELETE文は、SELECT文と同様に多相性を伴って動作します。つまり、ビューに対して実行されたUPDATEまたはDELETE文で抽出される行の集合には、指定されたビューのすべてのサブビューにある修飾行も明示的に含まれます。

たとえば、Person_vから全員を削除する次の文により、Student_vから学生全員が削除され、Employee_vビューから従業員全員が削除されます。

DELETE FROM Person_v;

サブビューを除外し、影響する行の対象を、指定されたビューに存在する人たちに限定するには、ONLYキーワードを使用します。たとえば、次の文は個人のみ更新し、従業員または学生は更新しません。

UPDATE ONLY(Person_v) SET address = ...


関連項目:

オブジェクト・ビュー階層、およびStudent_vEmployee_vを定義する例は、「オブジェクト・ビューの階層」を参照してください。

ビューにおけるネストした表列の更新

ネストした表を更新するには、新しい要素を挿入したり、既存の要素を更新または削除します。ビューの場合と同様に、仮想または合成のネストした表列は、通常は更新できません。この問題に対処するために、この列の上にINSTEAD OFトリガーを作成できます。

(ビューの)ネストした表列に対して定義されたINSTEAD OFトリガーは、列を変更するときに起動します。親である行の更新によってコレクション全体が置換される場合は、ネストした表列のINSTEAD OFトリガーは起動されないため注意してください。

変更および妥当性チェックを制御するINSTEAD OFトリガーの使用

INSTEAD OFトリガーを使用することで、他の方法では更新できない複雑なビューを更新できます。また制約の施行、権限のチェック、DML文の妥当性チェックにも、このトリガーを使用できます。これらのトリガーを使用すると、オブジェクト・ビューを介して作成されたオブジェクトに挿入、更新および削除が行われた結果としての変更を制御できます。

たとえば、ある部門の従業員の数を10以下とするという条件を施行する場合は、従業員ビュー用のINSTEAD OFトリガーを記述できます。ビューは更新できるため、DML文の実行にトリガーは必要ありませんが、制約を施行するにはトリガーが必要です。

例6-8に、SQL文を使用してトリガーを実装する方法を示します。

例6-8 ビューのINSTEAD OFトリガーの作成

-- Requires Ex. 6-2, Ex. 6-4, and Ex. 6-6 
CREATE TRIGGER emp_instr INSTEAD OF INSERT on emp_view 
FOR EACH ROW
DECLARE
  dept_var dept_t;
  emp_count integer;
BEGIN
  -- Enforce the constraint
  -- First get the department number from the reference
  UTL_REF.SELECT_OBJECT(:NEW.deptref, dept_var);

  SELECT COUNT(*) INTO emp_count
    FROM emp
   WHERE deptno = dept_var.deptno;
  IF emp_count < 9 THEN
     -- Do the insert
     INSERT INTO emp (empno, empname, salary, deptno) 
        VALUES (:NEW.empno, :NEW.ename, :NEW.salary, dept_var.deptno);
  END IF;
END;
/

オブジェクト・モデルのリモート表への適用

リモート表は、オブジェクト表のように直接アクセスできませんが、オブジェクト・ビューでは、あたかもオブジェクト表であるかのように、リモート表にアクセスできます。

ワシントンD.C.およびシカゴに支店を持つ会社について考えてみます。各支店に、従業員表があります。ワシントンの本社には、すべての部門のリストを持つ部門表があります。組織全体のビューを作成するには、個々のリモート表の上にビューを作成し、次に組織全体のビューを作成します。

このためには、次の手順を実行する必要があります。

  • listener.oraのエントリ((ADDRESS=(PROTOCOL=tcp) (HOST=stadv07.us.example.com)(PORT=1640))など)を更新します。

  • tnsnames.oraにエントリ(chicago=(DESCRIPTION= (ADDRESS=(PROTOCOL=ipc)(KEY=linux)) (CONNECT_DATA=(SERVICE_NAME=linux.regress.rdbms.dev.us.example.com)))など)を追加します。

  • 例6-9に示すようなCREATE DATABASE LINKコードを入力します。

例6-9では、各従業員表のオブジェクト・ビューを作成してから、グローバル・ビューを作成しています。

例6-9 リモート表にアクセスするためのオブジェクト・ビューの作成

-- Requires Ex. 6-2, Ex. 6-4, and Ex. 6-6 
-- Example requires DB links, such as these, modify for your use and uncomment
-- CREATE DATABASE LINK chicago CONNECT TO hr IDENTIFIED BY hr USING 'inst1';
-- CREATE DATABASE LINK washington CONNECT TO hr IDENTIFIED BY hr USING 'inst1';
CREATE VIEW emp_washington_view (eno, ename, salary, job)
   AS SELECT e.empno, e.empname, e.salary, e.job
          FROM emp@washington e;

CREATE VIEW emp_chicago_view (eno, ename, salary, job)
   AS SELECT e.empno, e.empname, e.salary, e.job
          FROM emp@chicago e;

CREATE VIEW orgnzn_view OF dept_t WITH OBJECT IDENTIFIER (deptno) 
    AS SELECT d.deptno, d.deptname,
          address_t(d.deptstreet,d.deptcity,d.deptstate,d.deptzip) AS deptaddr,
          CAST( MULTISET (
                      SELECT e.eno, e.ename, e.salary, e.job
                      FROM emp_washington_view e) 
                   AS employee_list_t) AS emp_list
       FROM dept d
       WHERE d.deptcity = 'Washington'
   UNION ALL
       SELECT d.deptno, d.deptname,
           address_t(d.deptstreet,d.deptcity,d.deptstate,d.deptzip) AS deptaddr,
              CAST( MULTISET (
                       SELECT e.eno, e.ename, e.salary, e.job
                       FROM emp_chicago_view e) 
                    AS employee_list_t) AS emp_list
       FROM dept d
       WHERE d.deptcity = 'Chicago';

このビューは、各部門のすべての従業員のリストを持ちます。従業員は複数の部門に所属できないため、UNION ALL句が使用されています。

オブジェクト・ビューにおける複雑なリレーションシップの定義

オブジェクト・ビューには、MAKE_REF演算子を使用して、循環参照を定義できます。つまり、view_Aview_Bを参照して、view_Bがview_Aを参照することもできます。これにより、リレーショナル・データで構成されるグラフのように複雑な構造を、オブジェクト・ビューが合成できるようになります。

たとえば、部門および従業員の例の場合、部門オブジェクトに従業員のリストが含まれています。領域を節約するために、部門オブジェクト内のすべての従業員を具体化せずに、従業員オブジェクトの参照を、部門オブジェクト内部に入れてみます。従業員オブジェクトへの参照を構成(確保)し、後でドット表記法を使用してこの参照に従い、従業員情報を抽出できます。

従業員オブジェクトはすでに従業員の所属部門への参照を持っているため、このモデルでのオブジェクト・ビューには、部門ビューと従業員ビューの間の循環参照が含まれます。

オブジェクト・ビュー間の循環参照は、次の2通りの方法で作成できます。


注意:

循環参照の作成方法ではいずれも、「循環参照を示す表および型」に示されている設定を実行する必要があります。

方法2の方が、手順が簡単ですが、ビューの作成時に、FORCEキーワードによりエラーが隠される可能性があります。ビュー作成中にエラーが発生したかどうかを確認するには、USER_ERRORSカタログ・ビューを問い合せる必要があります。ビュー作成文の中にエラーがないことが保証されている場合にのみ、この方法を使用してください。

また、使用時にエラーによってビューが再コンパイルできない場合は、ALTER VIEW COMPILEコマンドを使用し、ビューを手動で再コンパイルする必要があります。

循環ビュー参照の作成では、いずれの方法を使用する場合も、あらかじめ次に説明する設定を実行してください。

循環参照を示す表および型

まず、リレーショナル表および対応付けられたオブジェクト型を設定する必要があります。表にオブジェクトが入っていても、この表はオブジェクト表ではありません。データ・オブジェクトにアクセスするために、後でオブジェクト・ビューを作成します。

emp表には、従業員情報が入っています。

例6-10 循環参照を示すemp表の作成

CREATE TABLE emp
(  empno    NUMBER PRIMARY KEY,
   empname  VARCHAR2(20),
   salary   NUMBER,
   deptno   NUMBER );

-- first create a dummy, that is, incomplete, department type, so emp_t type
-- created later will succeed 

CREATE TYPE dept_t;
/

-- Create the employee type with a reference to the department, dept_t:
CREATE TYPE emp_t AS OBJECT
( eno NUMBER,
  ename VARCHAR2(20),
  salary  NUMBER,
  deptref REF dept_t );
/

-- Represent the list of references to employees as a nested table:
CREATE TYPE employee_list_ref_t AS TABLE OF REF emp_t;
/

-- Create the department table as a relational table
CREATE TABLE dept
(   deptno        NUMBER PRIMARY KEY,
    deptname      VARCHAR2(20),
    deptstreet    VARCHAR2(20),
    deptcity      VARCHAR2(10),
    deptstate     CHAR(2),
    deptzip       VARCHAR2(10) );

-- Create object types that map to columns from the relational tables:
CREATE TYPE address_t AS OBJECT 
( street        VARCHAR2(20),
   city         VARCHAR2(10),
   state        CHAR(2),
   zip          VARCHAR2(10));
/

-- Fill in the definition for dept_t, the incomplete type you previously created:
CREATE OR REPLACE TYPE dept_t AS OBJECT
( dno           NUMBER,
  dname         VARCHAR2(20),
  deptaddr      address_t,
  empreflist    employee_list_ref_t);
/

例6-10に示すように、emp表を作成してから、ダミーの部門型dept_tを作成する必要があります(これにより、作成したemp_t型が正常に機能できるようになります)。その後、dept_tへの参照を持つemp_tを作成します。ネストした表employee_list_ref_tとして従業員への参照のリストを作成し、部門表deptを作成します。次に、リレーショナル表にマップする列を持つオブジェクト型address_tを作成し、最後に不完全なdept_tの定義に埋め込みます。

使用できるデータ例を次に示します。

insert into emp values(1,'John','900',100);
insert into emp values(2,'james','1000',100);
insert into emp values(3,'jack',2000,200);

循環参照を持つオブジェクト・ビューの作成

前の項で基礎となるリレーショナル表が完成したので、今度はこのリレーショナル表にオブジェクト・ビューを作成します。

方法1: 2つ目のビューを作成した後、最初のビューを再作成する

まず、deptref列にNULLを持つ従業員ビューを作成します。後で、その列を参照に変えることができます。

次に、従業員オブジェクトへの参照を含む部門ビューを作成します。これにより、従業員オブジェクト全体を含めるのではなく、従業員オブジェクトへの参照のリストを作成します。

次に、部門ビューへの参照を持つ従業員ビューを再作成します。

例6-11 循環参照を持つオブジェクト・ビューの作成、方法1

-- Requires Ex. 6-10 
CREATE VIEW emp_view OF emp_t WITH OBJECT IDENTIFIER(eno)
   AS SELECT e.empno, e.empname, e.salary, NULL
         FROM emp e;

-- create department view, including references to the employee objects
CREATE VIEW dept_view OF dept_t WITH OBJECT IDENTIFIER(dno) 
   AS SELECT d.deptno, d.deptname, 
                address_t(d.deptstreet,d.deptcity,d.deptstate,d.deptzip),
                CAST( MULTISET (
                           SELECT MAKE_REF(emp_view, e.empno)
                           FROM emp e 
                           WHERE e.deptno = d.deptno) 
                        AS employee_list_ref_t)
   FROM dept d; 

CREATE OR REPLACE VIEW emp_view OF emp_t WITH OBJECT IDENTIFIER(eno)
   AS SELECT e.empno, e.empname, e.salary, 
                       MAKE_REF(dept_view, e.deptno)
         FROM emp e;

これでビューが作成されました。

方法2: FORCEキーワードを使用して最初のビューを作成する

ビューを作成する文に構文エラーがないという確信があれば、FORCEキーワードを使用して、他のビューを存在させずに、最初のビューを強制的に作成できます。

まず、この時点で存在しない部門ビューへの参照を含む従業員ビューを作成します。部門ビューが適切に作成されるまで、このビューの問合せは実行できません。

次に、従業員オブジェクトへの参照を含む部門ビューを作成します。emp_viewがすでに存在するため、ここでFORCEキーワードを使用する必要はありません。これにより、部門ビューの問合せを実行し、ネストした表empreflistからの従業員参照を解除することで従業員オブジェクトを取得できます。


注意:

すでに例6-11を実行した場合は、作成したビューを削除してから例6-12を実行してください。

例6-12 FORCEを持つビューの作成、方法2

-- Requires Ex. 6-10
-- create employee view
CREATE OR REPLACE FORCE VIEW emp_view OF emp_t WITH OBJECT IDENTIFIER(eno)
   AS SELECT e.empno, e.empname, e.salary, 
                       MAKE_REF(dept_view, e.deptno)
         FROM emp e;

-- create a department view that includes references to the employee objects
CREATE OR REPLACE VIEW dept_view OF dept_t WITH OBJECT IDENTIFIER(dno) 
   AS SELECT d.deptno, d.deptname, 
                address_t(d.deptstreet,d.deptcity,d.deptstate,d.deptzip),
                CAST( MULTISET (
                           SELECT MAKE_REF(emp_view, e.empno)
                           FROM emp e 
                           WHERE e.deptno = d.deptno) 
                        AS employee_list_ref_t)
   FROM   dept d; 

-- Querying with DEREF method 
SELECT DEREF(e.COLUMN_VALUE)
  FROM TABLE( SELECT e.empreflist FROM dept_view e WHERE e.dno = 100) e;

COLUMN_VALUEは、スカラーのネストした表の中のスカラー値を表す特別な名前です。この場合、COLUMN_VALUEはネストした表empreflistの中にある従業員オブジェクトの参照を示しています。

名前がJohnで始まるすべての従業員の従業員番号のみにアクセスすることも可能です。

例6-13 COLUMN_VALUEを使用した問合せ

-- Requires Ex. 6-10 and 6-12
SELECT e.COLUMN_VALUE.eno
  FROM TABLE(SELECT e.empreflist FROM dept_view e WHERE e.dno = 100) e
 WHERE e.COLUMN_VALUE.ename like 'John%';

表形式で出力するには、部門表をネストした表の項目と結合し、参照のリストのネストを解除します。

例6-14 COLUMN_VALUEを使用した問合せ、参照のネスト解除

-- Requires Ex. 6-10 and 6-12SELECT d.dno, e.COLUMN_VALUE.eno, e.COLUMN_VALUE.ename
  FROM dept_view d, TABLE(d.empreflist) e
 WHERE e.COLUMN_VALUE.ename like 'John%' 
  AND d.dno = 100;

最後に、dept_viewのかわりにemp_viewを使用するように、前述の問合せを書きなおし、ビュー間をナビゲートする方法を紹介します。

例6-15 COLUMN_VALUEを使用した問合せ、emp_viewの問合せ

-- Requires Ex. 6-10 and 6-12
SELECT e.deptref.dno, DEREF(f.COLUMN_VALUE)
  FROM emp_view e, TABLE(e.deptref.empreflist) f
 WHERE e.deptref.dno = 100 
  AND f.COLUMN_VALUE.ename like 'John%';

オブジェクト・ビューの階層

オブジェクト・ビュー階層は、オブジェクト・ビューの集合で、型階層内の様々な型が、それぞれのオブジェクト・ビューの基になっています。ビュー階層内のサブビューは、型階層内のサブタイプがスーパータイプの下に作成される場合と同じ方法で、スーパービューの下に作成されます。

ビュー階層内のそれぞれのオブジェクト・ビューは、1つの型のオブジェクトと一緒に移入されますが、任意のビューについての問合せでは、そのサブビューも暗黙的に扱います。このように、オブジェクト・ビュー階層により、一定のレベルまたはそれ以上のレベルに特化されたオブジェクトの多相集合を返す問合せを、簡単に組み立てられます。

person_typをルートとする、次のような型階層について考えてみます。

図6-1 オブジェクト型階層

図6-1の説明
「図6-1 オブジェクト型階層」の説明

オブジェクト・ビューをそれぞれの型に基づいて作成し、この型階層に基づいてオブジェクト・ビュー階層を作成しておくと、目的の特化レベルと対応するオブジェクト・ビューの問合せができます。たとえば、student_typのビューについて問い合せ、学生(定時制の学生を含む)のみ入っている結果セットを取得できます。

型階層内のいずれの型も、オブジェクト・ビュー階層のルート・ビューの基として使用できます。つまり、オブジェクト・ビュー階層をルート型から組み立てる必要はありません。また、オブジェクト・ビュー階層を型階層の各リーフまで拡張したり、すべてのブランチを範囲に入れる必要もありません。ただし、下位に介在するサブタイプは省略できません。いずれのサブビューも、直系スーパービューの型の直系サブタイプに基づく必要があります。

型が兄弟関係にあるサブタイプを2つ以上持てるように、オブジェクト・ビューも兄弟関係にあるサブビューを2つ以上持てます。ただし、任意の型に基づくサブビューが関与できるのは、1つのオブジェクト・ビュー階層のみです。2つの異なるオブジェクト・ビュー階層に、同じサブタイプに基づく1つのサブビューが存在することはありません。

サブビューはスーパービューからオブジェクト識別子(OID)を継承します。任意のサブビューに、OIDを明示的に指定することは許可されません。

ルート・ビューでは、WITH OBJECT ID句を使用して、オブジェクト識別子を明示的に指定できます。OIDがシステムによって生成される場合、またはルート・ビューに句を指定しない場合、サブビューを作成できるのは、同様にシステム生成のOIDを使用する表またはビューがルート・ビューの基になっている場合のみです。

ビューの基礎となる問合せにより、ビューが更新可能かどうかが決まります。ビューの問合せに、結合、集合演算子、集計ファンクション、GROUP BY句、DISTINCT句、疑似列または式を何も入れないというのが、ビューを更新可能にするための条件です。サブビューについても同じ条件が適用されます。

ビューが更新可能でなくても、適切なDML処理を実行するためのINSTEAD OFトリガーが定義できます。サブビューはINSTEAD OFトリガーを継承しない点に注意してください。

ビュー階層内のすべてのビューは、同じスキーマに所属する必要があります。


注意:

インスタンス化不可の型のビューを作成できます。インスタンス化不可の型は、インスタンスを持つことはできないので、このような型のオブジェクト・ビューを作成しても意味がありません。ただし、インスタンス化不可の型は、インスタンス化可能なサブタイプを持つことができます。インスタンス化不可の型のオブジェクト・ビューを作成できるため、インスタンス化不可の型が含まれている型階層をオブジェクト・ビュー階層の基にすることができます。

オブジェクト・ビュー階層の作成

オブジェクト・ビュー階層は、ルート・ビューの下にサブビューを作成して、構築します。このためには、例6-17のようにCREATE VIEW文でUNDERキーワードを使用します。

基礎となる様々な記憶域モデルが、同じオブジェクト・ビュー階層の基になることもあります。つまり、基礎となる表の様々なレイアウトや設計により、同じオブジェクト・ビュー階層が作成される可能性があるということです。基礎となる記憶域モデルの設計は、オブジェクト・ビュー階層のパフォーマンスや更新可能性に影響を与えます。

この項では、考えられる3種類の記憶域モデルを示します。最初のフラット・モデルの場合、オブジェクト・ビュー階層内のすべてのビューの基は、同じ表です。2番目の水平モデルの場合、それぞれのビューは、それぞれ別の表と1対1で対応します。3番目の垂直モデルの場合は、結合を使用してビューが構成されます。

これらの記憶域モデルを実行するには、まず例6-16に示すような型を作成します。

例6-16 記憶域モデル例に使用する型の作成

CREATE TYPE person_typ AS OBJECT
( ssn NUMBER,
  name VARCHAR2(30),
  address VARCHAR2(100)) NOT FINAL;/

CREATE TYPE student_typ UNDER person_typ 
( deptid NUMBER,
   major VARCHAR2(30)) NOT FINAL;/

CREATE TYPE employee_typ UNDER person_typ
( empid NUMBER, 
  mgr VARCHAR2(30));/

この項の内容は次のとおりです。

フラット・モデル

フラット・モデルの場合、オブジェクト・ビュー階層内のすべてのビューの基となるのは、同じ表です。次に例に示すAllPersonsという1つの表には、person_typstudent_typおよびemployee_typのすべての属性の列が格納されます。

図6-2 オブジェクト・ビュー階層のフラット記憶域モデル

図6-2の説明
「図6-2 オブジェクト・ビュー階層のフラット記憶域モデル」の説明

それぞれの行の型は、typeid列により識別されます。これらの有効な値は、例6-16で作成した型(1 = person_typ、2 = student_typ、 3 = employee_typ)です。

例6-17では、表AllPersonsを作成してから、オブジェクト・ビュー階層を構成するビューを作成します。

例6-17 オブジェクト・ビュー階層の作成

-- Requires Ex. 6-16
CREATE TABLE AllPersons
( typeid NUMBER(1), 
  ssn NUMBER, 
  name VARCHAR2(30), 
  address VARCHAR2(100),
  deptid NUMBER,
  major VARCHAR2(30),
  empid NUMBER, 
  mgr VARCHAR2(30));

CREATE VIEW Person_v OF person_typ
  WITH OBJECT OID(ssn) AS
  SELECT ssn, name, address 
  FROM AllPersons 
  WHERE typeid = 1;

CREATE VIEW Student_v OF student_typ UNDER Person_v
  AS 
  SELECT ssn, name, address, deptid, major
  FROM AllPersons
  WHERE typeid = 2;

CREATE VIEW Employee_v OF employee_typ UNDER Person_v
  AS
  SELECT ssn, name, address, empid, mgr
  FROM AllPersons
  WHERE typeid = 3;

単純である、という点がフラット・モデルの利点です。また、フラット・モデルが、索引や制約への対応に支障をきたす原因になることはありません。このモデルのデメリットは、次のとおりです。

  • 1つの表に格納できる列は1000以下のため、フラット・モデルでは、オブジェクト・ビュー階層に格納できる列の合計数が、1000列に制限されます。

  • 表の各列には、オブジェクト・ビューの型に属さないすべての属性のかわりにNULLが入ってしまいます。このようにNULLが後続しない状態は、パフォーマンスを低下させる可能性があります。

水平モデル

水平モデルの場合、それぞれのビューまたはサブビューの基になるのは、異なる表です。例に示す表はリレーショナル表ですが、これらの表は、列の代入性が無効になるオブジェクト表になる場合もあります。

図6-3 オブジェクト・ビュー階層の水平記憶域モデル

図6-3の説明
「図6-3 オブジェクト・ビュー階層の水平記憶域モデル」の説明

例6-18では、表を作成してから、これらの表に基づくビューを作成します。

例6-18 表の水平モデルの作成

-- Requires Ex. 6-16 and Ex. 6-17
CREATE TABLE only_persons
( ssn NUMBER,
  name VARCHAR2(30),
  address VARCHAR2(100));

CREATE TABLE only_students
( ssn NUMBER, 
  name VARCHAR2(30),
  address VARCHAR2(100), 
  deptid NUMBER,  
  major VARCHAR2(30));

CREATE TABLE only_employees
( ssn NUMBER, 
  name VARCHAR2(30),
  address VARCHAR2(100), 
  empid NUMBER, 
  mgr VARCHAR2(30));

CREATE OR REPLACE VIEW Person_v OF person_typ
  WITH OBJECT OID(ssn) AS
  SELECT * 
  FROM only_persons; 

CREATE OR REPLACE VIEW Student_v OF student_typ UNDER Person_v
  AS 
  SELECT *
  FROM only_students;

CREATE OR REPlACE VIEW Employee_v OF employee_typ UNDER Person_v
  AS
  SELECT * 
  FROM only_employees;

水平モデルを使用すると、非常に効率的なのは、次のような構成の問合せです。

例6-19 ビューの水平モデルの問合せ

-- Requires Ex. 6-16 and Ex. 6-17
-- add the following data
insert into only_persons values(1234,'John','abc');
insert into only_students values(1111,'James','abc',100,'CS');
insert into only_employees values(2222,'jack','abc',400,'Juliet');

SELECT VALUE(p) FROM Person_v p
  WHERE VALUE(p) IS OF (ONLY student_typ);

OUTPUT:
VALUE(P)(SSN, NAME, ADDRESS)
--------------------------------------------------------------------------------
STUDENT_TYP(1111, 'James', 'abc', 100, 'CS')

このような問合せでは、特定の型のオブジェクトをすべて取得するために、1つの物理表にアクセスするのみですみます。このモデルのデメリットは、SELECT * FROM viewのような問合せでは、基礎となるすべての表に対してUNIONを実行し、指定されたビューの列に対してのみ行を投影する必要があることです。(「階層内のビューの問合せ」を参照してください。)また、属性(および一意制約)についての索引は複数の表をまたがる必要がありますが、現在この機能はサポートされていません。

垂直モデル

垂直モデルの場合、階層内のそれぞれのビューと対応する物理表が存在しますが、物理表に格納されるのは、対応するサブタイプに固有の属性のみです。

図6-4 オブジェクト・ビュー階層の垂直記憶域モデル

図6-4の説明
「図6-4 オブジェクト・ビュー階層の垂直記憶域モデル」の説明

例6-20では、表を作成してから、対応するビューを作成します。

例6-20 表およびビューの垂直モデルの作成

CREATE TABLE all_personattrs 
( typeid NUMBER,
  ssn NUMBER,  
  name VARCHAR2(30),
  address VARCHAR2(100));

CREATE TABLE all_studentattrs
( ssn NUMBER, 
  deptid NUMBER, 
  major VARCHAR2(30));

CREATE TABLE all_employeeattrs
( ssn NUMBER,
  empid NUMBER, 
  mgr VARCHAR2(30));

CREATE OR REPLACE VIEW Person_v OF person_typ 
WITH OBJECT OID(ssn) AS
  SELECT ssn, name, address 
  FROM all_personattrs 
  WHERE typeid = 1;

CREATE OR REPLACE VIEW Student_v OF student_typ UNDER Person_v
  AS 
  SELECT x.ssn, x.name, x.address, y.deptid, y.major
  FROM all_personattrs x, all_studentattrs y
  WHERE x.typeid = 2 AND x.ssn = y.ssn;

CREATE OR REPLACE VIEW Employee_v OF employee_typ UNDER Person_v
  AS
  SELECT x.ssn, x.name, x.address, y.empid, y.mgr
  FROM all_personattrs x, all_employeeattrs y
  WHERE x.typeid = 3 AND x.ssn = y.ssn;

垂直モデルでは、SELECT * FROM root_viewのような問合せが効率的に処理され、また、個々の属性に索引を付けたり、属性に一意制約を付けることができます。ただし、型のインスタンスを再作成するには、それぞれのレベルについて、階層のルートから型を削除するオブジェクト識別子(OID)の結合を実行する必要があります。

階層内のビューの問合せ

オブジェクト・ビュー階層内の任意のビューまたはサブビューに対し、問合せを実行できます。問合せにより戻されるのは、問い合せたビューの宣言された型の行と、その型のサブタイプの行です。たとえば、person_typ型階層が基になっているオブジェクト・ビュー階層では、person_typのビューを問い合せて、学生や従業員を含む個人全員が格納されている結果セットを取得したり、student_typのビューを問い合せて、学生(定時制の学生を含む)のみ格納されている結果セットを取得できます。

問合せのSELECT構文のリストには、オブジェクト・インスタンスを戻すREF()VALUE()のようなファンクションを含めるか、またはperson_typnamessn属性のようなビューの宣言された型のオブジェクト属性を指定できます。

オブジェクト・インスタンスを戻すためにファンクションを指定すると、問合せにより多相結果セットが戻されます。つまり、ビューの宣言された型のインスタンスと、その型のサブタイプのインスタンスのどちらも戻されます。

たとえば、次の問合せでは、あらゆる型の個人、従業員および学生のインスタンスのみでなく、これらのインスタンスREFも戻されます。

例6-21 REFと値での問合せ

-- Requires Ex. 6-20
insert into all_personattrs values(1,1111,'John','abc');
insert into all_personattrs values(2,2222,'Jack','def');
insert into all_personattrs values(3,3333,'James','ghi');
insert into all_studentattrs values(2222,100,'CS');
insert into all_employeeattrs values(3333,444,'Julia');
SELECT REF(p), VALUE(p) FROM Person_v p;

OUTPUT:
REF(P)
--------------------------------------------------------------------------------
VALUE(P)(SSN, NAME, ADDRESS)
--------------------------------------------------------------------------------
00004A038A00465A6E6E779EC1F25FE040578CE70A447E0000001426010001000100290000000000
090600812A00078401FE0000000B03C20C0C00000000000000000000000000000000000000
PERSON_TYP(1111, 'John', 'abc')
 
00004A038A00465A6E6E779EC1F25FE040578CE70A447E0000001426010001000100290000000000
090600812A00078401FE0000000B03C2222200000000000000000000000000000000000000
EMPLOYEE_TYP(3333, 'James', 'ghi', 444, 'Julia')
 
00004A038A00465A6E6E779EC1F25FE040578CE70A447E0000001426010001000100290000000000
 
REF(P)
--------------------------------------------------------------------------------
VALUE(P)(SSN, NAME, ADDRESS)
--------------------------------------------------------------------------------
090600812A00078401FE0000000B03C2171700000000000000000000000000000000000000
STUDENT_TYP(2222, 'Jack', 'def', 100, 'CS')

ビューの宣言された型の個々の属性をSELECT構文のリストに指定した場合、またはSELECT *を実行した場合も、ビューの宣言された型およびこの型のサブタイプの行が戻されます。ただし、これらの行が投影される先は、ビューの宣言された型の属性の列で、使用されるのは、これらの列のみです。つまり、サブタイプを表現する対象は、ビューの宣言された型からサブタイプが継承した属性、およびサブタイプがビューの宣言された型と共有する属性のみです。

たとえば、次の問合せでは、個人全員の行およびあらゆる型の従業員および学生の行が戻されますが、結果に使用されるのは、person_typの属性(namessnおよびaddress)の列のみです。student_typdeptid属性などサブタイプに追加された属性の行は、何も表示されません。

SELECT * FROM Person_v;

結果からサブビューを除外するには、ONLYキーワードを使用します。ONLYキーワードにより、選択対象が、問合せ対象のビューの宣言された型に限定されます。

SELECT VALUE(p) FROM ONLY(Person_v) p;

ビュー階層についての操作の権限

通常、サブビューを持つビューについての問合せで要求されるのは、参照対象のビューについてのSELECT権限のみで、サブビューについての明示的な権限は要求されません。次に示す問合せでは、Person_vについてのSELECT権限のみ要求され、このビューのサブビューについての権限は要求されません。

SELECT * FROM Person_v;

ただし、サブタイプの属性として追加されていても、ルート型では使用しない属性を選択する問合せの場合は、サブビューについてのSELECT権限も要求されます。このようなサブタイプ属性には、別のアクセス権限を適度に必要とする機密情報が格納されている可能性があります。

次に示す問合せでは、オブジェクト・インスタンスが選択され、その結果、サブタイプのすべての属性が取り出されるため、Person_vStudent_vEmployee_v(およびPerson_vのサブビュー)についてのSELECT権限が要求されます。

SELECT VALUE(p) FROM Person_v p;

ビュー階層全体についてのSELECT権限を付与する処理を簡素化するには、HIERARCHYオプションが使用できます。ビューについてのSELECT権限をユーザーに付与するときに、HIERARCHYオプションを指定すると、現在および将来存在するビューのすべてのサブビューについてのSELECT権限も暗黙的に付与されます。次に例を示します。

GRANT SELECT ON Person_v TO user WITH HIERARCHY OPTION;

サブビューに属する行を除外する問合せには、すべてのサブビューについてのSELECT権限も要求されます。その理由は、インスタンスの最も狭い意味での型にのみ属している行の情報は、機密情報の可能性があり、すべての行をサブビューから除外する問合せ(次に示すような問合せ)の場合は、サブビューについてのSELECT権限が要求されるためです。

SELECT * FROM ONLY(Person_v);