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

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

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

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

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

注意:

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

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

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

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

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

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

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);

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

循環参照を持つオブジェクト・ビューを作成できます。

「オブジェクト・ビューにおける複雑なリレーションシップの定義」の説明に従って基礎となるリレーショナル表定義を設定した場合、その定義に基づいてオブジェクト・ビューを作成できます。

内容は次のとおりです。

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

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;

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

6.13.2.2 方法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-12 
SELECT 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%';