この付録では、ユーザー定義データ型(Oracleオブジェクト)の作成および使用方法の概要を説明するサンプル・アプリケーションを示します。まず、アプリケーションの開発にリレーショナル・モデルを使用し、次にオブジェクト・リレーショナル・モデルを使用します。
内容は次のとおりです。
ユーザー定義型とは、アプリケーション内のデータ構造および操作を形式化したスキーマ・オブジェクトのことです。
この付録の例では、オブジェクト型の定義、使用および進化における最も重要な点を説明します。オブジェクト型の作業で重要な点の1つは、オブジェクトに対して操作を実行するメソッドを作成することです。この例では、オブジェクト型メソッドの定義でPL/SQL言語を使用します。型の定義など、オブジェクト型の使用にかかわるそれ以外の部分ではSQLを使用します。
例では、顧客の発注書を管理するアプリケーションについて、様々なバージョンのデータベース・スキーマを作成します。まず、純粋なリレーショナル・バージョンを示し、次に同等のオブジェクト・リレーショナル・バージョンを示します。いずれのバージョンでも、顧客、発注書、明細項目などの同じ基本エンティティが使用されています。ただし、オブジェクト・リレーショナル・バージョンでは、これらのエンティティに対してオブジェクト型を作成し、それぞれのオブジェクト型のインスタンスをインスタンス化することによって、特定の顧客および発注書のデータを管理します。
PL/SQLおよびJavaを使用すると、特にコレクション要素のアクセスおよび操作の点で、この付録に示す以上の機能が実現できます。
OCI、Pro*C/C++、OO4OまたはOracle Data Providers for .NET(ODP.NET)を使用するクライアント・アプリケーションでは、その広範な機能を利用してオブジェクトおよびコレクションにアクセスし、それらをクライアント上で操作できます。
関連項目:
|
この項では、図A-1に示す発注書スキーマのリレーショナル・バージョンを実装します。
この例における基本エンティティは、次のとおりです。
顧客
販売する製品の在庫
発注書
図A-1のように、顧客には担当情報があるため、所在地と電話番号セットはその顧客に対して排他的です。アプリケーションでは、同じ所在地または電話番号に他の顧客を関連付けることはできません。顧客が所在地を変更すると、前の所在地は存在しなくなります。ユーザーが顧客でなくなった場合は、関連所在地が存在しなくなります。
顧客は、発注書に対して1対多リレーションシップを持ちます。1件の顧客が多数の発注を行うことはできますが、特定の発注を行う顧客は1件のみです。顧客は発注前に定義できるため、リレーションシップは必須ではなくオプションです。
同様に、発注書は在庫品目に対して多対多リレーションシップを持ちます。このリレーションシップはどの発注書にどの在庫品目が記載されているかを示すものではないため、E-Rには明細項目の表記法が使用されます。発注書には、1つ以上の明細項目を含める必要があります。各明細項目は、1件の発注書にのみ関連付けられています。明細項目と在庫品目の間には、在庫品目は明細書に記載しないか、1件または多数の明細項目に記載できますが、各明細項目が参照する在庫品目は1つのみであるというリレーションシップがあります。
リレーショナル・アプローチでは、すべてが表に正規化されます。表名は、Customer_reltab
、PurchaseOrder_reltab
およびStock_reltab
です。
所在地の各部はCustomer_reltab
表の列となります。電話番号を列として構造化することで、顧客が使用できる電話番号の数について任意の制限が設定されます。
リレーショナル・アプローチでは、明細項目が発注書から分離され、それぞれがPurchaseOrder_reltab
およびLineItems_reltab
という固有の表に格納されます。
図A-1のように、明細項目は発注書および在庫品目の両方とリレーションシップを持ちます。これらは、PurchaseOrder_reltab
およびStock_reltab
への外部キーを使用してLineItems_reltab
表に列として実装されます。
注意: この項の表記規則では、リレーショナル表の名前に接尾辞_reltab が追加されています。このようにわかりやすい表記法を使用することで、コードを容易にメンテナンスできます。
表( |
以降の各項では、リレーショナル・アプローチに基づいて作成された表について説明します。
Customer_reltab
表の定義は、次のとおりです。
例A-1 Customer_reltab表の作成
CREATE TABLE Customer_reltab ( CustNo NUMBER NOT NULL, CustName VARCHAR2(200) NOT NULL, Street VARCHAR2(200) NOT NULL, City VARCHAR2(200) NOT NULL, State CHAR(2) NOT NULL, Zip VARCHAR2(20) NOT NULL, Phone1 VARCHAR2(20), Phone2 VARCHAR2(20), Phone3 VARCHAR2(20), PRIMARY KEY (CustNo));
この表Customer_reltab
には、顧客に関するすべての情報、つまり、顧客固有の情報(NOT
NULL
制約で定義)と重要でない情報がすべて格納されます。この表の定義によると、アプリケーションでは各顧客に出荷先所在地が必要です。
エンティティ関連(E-R)ダイアグラムは発注する顧客を示していましたが、この表では顧客と発注書の間のリレーションシップは許可されていません。このリレーションシップは発注書別に管理する必要があります。
PurchaseOrder_reltab
表の定義は、次のとおりです。
例A-2 PurchaseOrder_reltab表の作成
CREATE TABLE PurchaseOrder_reltab ( PONo NUMBER, /* purchase order no */ Custno NUMBER references Customer_reltab, /* Foreign KEY referencing customer */ OrderDate DATE, /* date of order */ ShipDate DATE, /* date to be shipped */ ToStreet VARCHAR2(200), /* shipto address */ ToCity VARCHAR2(200), ToState CHAR(2), ToZip VARCHAR2(20), PRIMARY KEY(PONo));
PurchaseOrder_reltab
では、Customer_reltab
のCustNo
キーを参照する外部キー(FK)列CustNo
を使用して顧客と発注書とのリレーションシップを管理します。PurchaseOrder_reltab
表には、関連明細項目に関する情報は含まれていません。次項の明細項目表では、発注番号を使用して明細項目を親発注書に関連付けます。
Stock_reltab
表の定義は、次のとおりです。
LineItems_reltab
表の定義は、次のとおりです。
例A-4 LineItems_reltab表の作成
CREATE TABLE LineItems_reltab ( LineItemNo NUMBER, PONo NUMBER REFERENCES PurchaseOrder_reltab, StockNo NUMBER REFERENCES Stock_reltab, Quantity NUMBER, Discount NUMBER, PRIMARY KEY (PONo, LineItemNo));
注意: Stock_reltab 表とPurchaseOrder_reltab 表は、LineItems_reltab 表の前に作成する必要があります。 |
表名には、この表に明細項目の集合が保持されることをコードを読むユーザーに対して強調するために、複数形であるLineItems_reltab
が使用されています。
エンティティ関連ダイアグラムに示すように、明細項目のリストは発注書および在庫品目の両方とのリレーションシップを持ちます。これらのリレーションシップは、LineItems_reltab
で次の2つの外部キー列を使用して管理されます。
PONo
: PurchaseOrder_reltab
のPONo
列を参照します。
StockNo
: Stock_reltab
のStockNo
列を参照します。
アプリケーションでは、次のような文を使用して表にデータを挿入します。
例A-5 在庫の設定
INSERT INTO Stock_reltab VALUES(1004, 6750.00, 2); INSERT INTO Stock_reltab VALUES(1011, 4500.23, 2); INSERT INTO Stock_reltab VALUES(1534, 2234.00, 2); INSERT INTO Stock_reltab VALUES(1535, 3456.23, 2);
例A-6 顧客の登録
INSERT INTO Customer_reltab VALUES (1, 'Jean Nance', '2 Avocet Drive', 'Redwood Shores', 'CA', '95054', '415-555-0102', NULL, NULL); INSERT INTO Customer_reltab VALUES (2, 'John Nike', '323 College Drive', 'Edison', 'NJ', '08820', '609-555-0190', '201-555-0140', NULL);
アプリケーションでは、次のような問合せを実行できます。
例A-9 特定の発注書に関する顧客および明細項目データの取得
SELECT C.CustNo, C.CustName, C.Street, C.City, C.State, C.Zip, C.phone1, C.phone2, C.phone3, P.PONo, P.OrderDate, L.StockNo, L.LineItemNo, L.Quantity, L.Discount FROM Customer_reltab C, PurchaseOrder_reltab P, LineItems_reltab L WHERE C.CustNo = P.CustNo AND P.PONo = L.PONo AND P.PONo = 1001;
アプリケーションでは、例A-13のような文を実行してデータを削除できます。
オブジェクト・リレーショナル・アプローチも、「エンティティとリレーションシップ」に示したのと同じE-Rから始まります。これらのE-Rを次のクラス図のようにオブジェクト指向の観点から見ると、実社会のさらに多くの構造をデータベース・スキーマに変換できます。
オブジェクト・リレーショナル・アプローチでは、所在地や複数の電話番号を関連性のないリレーショナル表列に分割するかわりに、所在地全体および電話番号リスト全体を表す型を定義します。同様に、このアプローチでは明細項目と発注書を個別に格納するかわりに、ネストした表を使用して格納します。
主なエンティティ(顧客、在庫および発注書)はオブジェクト型になります。オブジェクト型間のなんらかのリレーションシップを表すためにオブジェクト参照が使用されます。コレクション型(VARRAYおよびネストした表)は、複数値属性をモデル化するために使用されます。
注意: この付録では、新しいオブジェクト・リレーショナル・スキーマを作成してオブジェクト・リレーショナル・インタフェースを実装します。このアプローチでは、データ格納用のオブジェクト表を作成します。または、オブジェクト表のかわりにオブジェクト・ビューを使用して、リレーショナル表に格納されている既存のデータへのオブジェクト・リレーショナル・インタフェースを実装することもできます。オブジェクト・ビューについては、第6章を参照してください。 |
オブジェクト型は、CREATE
TYPE
文で作成します。たとえば、次の文では、StockItem_objtyp
型が作成されます。
例A-14 StockItem_objtypオブジェクトの作成
CREATE TYPE StockItem_objtyp AS OBJECT ( StockNo NUMBER, Price NUMBER, TaxRate NUMBER ); /
StockItem_objtyp
型のインスタンスは、顧客が発注する在庫品目を表すオブジェクトです。これらは3つの数値属性を持ちます。StockNo
は主キーです。
型を定義する順序によって違いが生じる場合があります。参照先の型を定義してから、その型を参照する他の型を定義するのが理想的です。
たとえば、LineItem_objtyp
型はStockItem_objtyp
のオブジェクトへのREF
である属性を含むことでStockItem_objtyp
を参照(前提と)します。このことは、LineItem_objtyp
型を作成する文で確認できます。
例A-15 LineItem_objtypオブジェクトの作成
CREATE TYPE LineItem_objtyp AS OBJECT ( LineItemNo NUMBER, Stock_ref REF StockItem_objtyp, Quantity NUMBER, Discount NUMBER ); /
LineItem_objtyp
型のインスタンスは、明細項目を表すオブジェクトです。これらは3つの数値属性と1つのREF
属性を持ちます。LineItem_objtyp
により明細項目エンティティがモデル化され、対応する在庫オブジェクトへのオブジェクト参照が含まれます。
型間の参照が入り組んでいるために、ある型を作成する前にその前提となる型をすべて作成することが困難な場合や不可能な場合があります。このような状況に対処するために、不完全型と呼ばれる型を作成し、参照先として作成する他の型のプレースホルダとして使用できます。これにより、他の型を作成した後に、不完全型に戻って完全型で置き換えることができます。
たとえば、StockItem_objtyp
の作成前にLineItem_objtyp
を作成する必要がある場合は、次のような文を使用してLineItem_objtyp
を不完全型として作成します。
CREATE
TYPE
LineItem_objtyp;
不完全型の作成に使用されるCREATE
TYPE
文の書式には、AS
OBJECT
句がなく、属性の指定もありません。
不完全型を完全な定義で置き換えるには、次の例に示すようにOR
REPLACE
句を挿入します。
例A-16 LineItem_objtypオブジェクトの置換
CREATE OR REPLACE TYPE LineItem_objtyp AS OBJECT ( LineItemNo NUMBER, Stock_ref REF StockItem_objtyp, Quantity NUMBER, Discount NUMBER ); /
OR
REPLACE
句は、置き換える不完全型がない場合も使用できます。
ここで、スキーマに必要な残りの型を作成します。次の文では、電話番号リストの配列型を定義します。
PhoneList_vartyp
型のデータ単位(インスタンス)は、それぞれがVARCHAR2
型のデータ項目で表される最大10個の電話番号のVARRAYです。
VARRAYまたはネストした表を使用して、電話番号リストを格納できます。この場合のリストは、1件の顧客の連絡先電話番号のセットです。次の理由から、VARRAYの方がネストした表よりも適しています。
番号の順序が重要な場合があります。VARRAYは順序付きですがネストした表には順序がありません。
特定の顧客の電話番号は少数です。VARRAYの場合は、最大要素数(この例で10)を事前に指定する必要があります。特別なサイズ制限のないネストした表よりも、記憶域が効率的に使用されます。
ネストした表の問合せはできますが、VARRAYの問合せはできません。ただし、電話番号リストを問い合せる理由はないため、ネストした表を使用してもメリットはありません。
通常、順序付けと境界が設計上の重要課題でなければ、設計者は次の経験則に従ってVARRAYとネストした表のどちらを使用するかを選択できます。つまり、コレクションの問合せが必要な場合はネストした表を使用し、コレクション全体を取り出す意図がある場合はVARRAYを使用します。
次の文では、所在地を表すオブジェクト型Address_objtyp
を定義します。
例A-18 Address_objtypオブジェクトの作成
CREATE TYPE Address_objtyp AS OBJECT ( Street VARCHAR2(200), City VARCHAR2(200), State CHAR(2), Zip VARCHAR2(20) ) /
所在地の属性はすべて、単純化された郵送先住所の各部を表す文字列です。
次の文では、他のオブジェクト型を構築ブロックとして使用するオブジェクト型Customer_objtyp
を定義します。
例A-19 Customer_objtypオブジェクトの作成
CREATE TYPE Customer_objtyp AS OBJECT ( CustNo NUMBER, CustName VARCHAR2(200), Address_obj Address_objtyp, PhoneList_var PhoneList_vartyp, ORDER MEMBER FUNCTION compareCustOrders(x IN Customer_objtyp) RETURN INTEGER ) NOT FINAL; /
Customer_objtyp
型のインスタンスは、特定の顧客に関する情報ブロックを表すオブジェクトです。Customer_objtyp
オブジェクトの属性は、数値、文字列、Address_objtyp
オブジェクトおよびPhoneList_vartyp
型のVARRAYです。
NOT
FINAL
句を使用すると、必要に応じて後からcustomer型のサブタイプを作成できます。デフォルトでは、型はFINAL
として作成されますが、これは、その型がサブタイプを導出することでそれ以上特化できないことを意味します。より特殊な顧客に関するCustomer_objtyp
のサブタイプの定義については、この付録で後述します。
各Customer_objtyp
オブジェクトには、2種類の比較メソッドの一方であるオーダー・メソッドも関連付けられています。Oracleでは、2つのCustomer_objtyp
オブジェクトの比較を必要とする場合に、暗黙的にcompareCustOrders
メソッドが起動されて比較が実行されます。
2種類の比較メソッドとは、マップ・メソッドとオーダー・メソッドです。このアプリケーションでは、説明のためにそれぞれ1つずつ使用します。
ORDER
メソッドは比較対象となる2つのオブジェクトごとにコールする必要がありますが、マップ・メソッドはオブジェクトごとに1回コールされます。通常、一連のオブジェクトをソートする場合、ORDER
メソッドのコール回数の方がマップ・メソッドのコール回数よりも多くなります。
関連項目:
|
次の文では、ネストした明細項目表の型を定義します。各発注書では、このネストした表の型のインスタンスを使用して、その発注書の明細項目が格納されます。
この型のインスタンスは、各行にLineItem_objtyp
型のオブジェクトが含まれるネストした表オブジェクト(ネストした表)です。次の理由から、複数値明細項目リストを表すには、LineItem_objtyp
オブジェクトのVARRAYよりもネストした明細項目表の方が適しています。
ほとんどのアプリケーションでは、明細項目の内容を問い合せる必要があります。明細項目がネストした表に格納されていれば、この問合せにSQLを使用できますが、VARRAYに格納されている場合はSQLを使用できません。
アプリケーションで明細項目データに索引付けする必要がある場合、ネストした表は使用できますが、VARRAYは使用できません。
明細項目の格納順序は重要でないと推定される場合、問合せ時に必要に応じて明細項目番号で順序付けできます。
発注書の明細項目数には、事実上の上限はありません。VARRAYを使用するには、要素数の任意の上限を指定する必要があります。
次の文では、オブジェクト型PurchaseOrder_objtyp
を定義します。
例A-21 PurchaseOrder_objtypオブジェクトの作成
CREATE TYPE PurchaseOrder_objtyp AUTHID CURRENT_USER AS OBJECT ( PONo NUMBER, Cust_ref REF Customer_objtyp, OrderDate DATE, ShipDate DATE, LineItemList_ntab LineItemList_ntabtyp, ShipToAddr_obj Address_objtyp, MAP MEMBER FUNCTION getPONo RETURN NUMBER, MEMBER FUNCTION sumLineItems RETURN NUMBER ); /
PurchaseOrder_objtyp
型のインスタンスは、発注書を表すオブジェクトです。この種のオブジェクトには、Customer_objtyp
へのREF
、Address_objtyp
オブジェクト、LineItem_objtyp
型に基づくLineItemList_ntabtyp
型のネストした表など、6つの属性があります。PONo
は主キーで、Cust_ref
は外部キーです。
PurchaseOrder_objtyp
型のオブジェクトには、2つのメソッドgetPONo
およびsumLineItems
があります。一方のgetPONo
は、2種類の比較メソッドの1つであるマップ・メソッドです。マップ・メソッドは、オブジェクト内のレコードの順序における指定されたレコードの相対位置を戻します。そのため、Oracleでは、2つのPurchaseOrder_objtyp
オブジェクトの比較が必要になると、暗黙的にgetPONo
メソッドがコールされて比較が実行されます。
2つのプラグマ宣言は、2つのメソッドに必要なデータベースへのアクセス・タイプ情報をPL/SQLに提供します。
文には、メソッドgetPONo
およびsumLineItems
を実装する実際のPL/SQLプログラムは含まれていません。「メソッド定義」を参照してください。
型にメソッドがない場合、その定義はCREATE
TYPE
文のみで構成されます。ただし、メソッドを持つ型の場合は、型の定義を完了するために型本体も定義する必要があります。そのためにはCREATE
TYPE
BODY
文を使用します。CREATE
TYPE
の場合と同様に、OR
REPLACE
句を挿入できます。既存の型本体を新規の型本体で置き換える場合は、メソッドを変更するために、この句を挿入する必要があります。
次の文では、PurchaseOrder_objtyp
型の本体を定義します。この文は、型のメソッドを実装するPL/SQLプログラムを提供します。
例A-22 PurchaseOrder_objtyp型本体の作成
CREATE OR REPLACE TYPE BODY PurchaseOrder_objtyp AS MAP MEMBER FUNCTION getPONo RETURN NUMBER is BEGIN RETURN PONo; END; MEMBER FUNCTION sumLineItems RETURN NUMBER is i INTEGER; StockVal StockItem_objtyp; Total NUMBER := 0; BEGIN FOR i in 1..SELF.LineItemList_ntab.COUNT LOOP UTL_REF.SELECT_OBJECT(LineItemList_ntab(i).Stock_ref,StockVal); Total := Total + SELF.LineItemList_ntab(i).Quantity * StockVal.Price; END LOOP; RETURN Total; END; END; /
getPONo
メソッドは、単にこのメソッドをコールするPurchaseOrder_objtyp
型のインスタンスのPONo
属性の値(発注書番号)を戻します。この種のget
メソッドを使用すると、内部表現に変更があった場合にも、オブジェクトを使用するコードを再加工する必要がありません。
sumLineItems
メソッドは、多数のオブジェクト・リレーショナル機能を使用します。
前述のように、sumLineItems
メソッドの基本機能は、関連PurchaseOrder_objtyp
オブジェクトの明細項目の値の合計を戻すことです。各ファンクションのパラメータとして暗黙的に作成されるSELF
キーワードにより、そのオブジェクトを参照できます。
COUNT
キーワードは、PL/SQL表または配列の要素数のカウントを示します。この例ではLOOP
と併用されており、アプリケーションはコレクション内の全要素(この場合は発注書の項目)を反復します。この方法で、SELF
.LineItemList_ntab
.COUNT
はネストした表内でPurchaseOrder_objtyp
オブジェクトのLineItemList_ntab
属性(ここではSELF
で表現)と一致する要素の数をカウントします。
実装内ではパッケージUTL_REF
のメソッドが使用されます。Oracleでは、PL/SQLプログラム内でのREF
の暗黙的な参照解除はサポートされないため、UTL_REF
メソッドが必要です。UTL_REF
パッケージは、オブジェクト参照を操作するメソッドを提供します。ここでは、Stock_ref
に対応するStockItem_objtyp
オブジェクトを取得するためにSELECT_OBJECT
メソッドがコールされます。
AUTHID
CURRENT_USER
構文では、PurchaseOrder_objtyp
が実行者権限を使用して定義されていることが指定されます。つまり、メソッドは型を定義したユーザーの権限ではなく現行のユーザーの権限で実行されます。
PL/SQL変数StockVal
はStockItem_objtyp
型です。この変数は、UTL_REF
.SELECT_OBJECT
により次を参照するオブジェクトに設定されます。
(LineItemList_ntab(i).Stock_ref)
このオブジェクトは、現在選択されている明細項目で参照される実際の在庫品目です。
問題の在庫品目を取り出した後、次のステップは原価を計算することです。プログラムでは、在庫品目の原価をStockItem_objtyp
オブジェクトのPrice
属性であるStockVal
.Price
として参照します。ただし、品目の原価を計算するために品目の受注数量も知る必要があります。アプリケーションでは、条件LineItemList_ntab(i)
.Quantity
は現在選択されているLineItem_objtyp
オブジェクトのQuantity
属性を表します。
メソッド・プログラムの残りの部分は、明細項目の値の合計を計算するループです。メソッドは合計を戻します。
次の文では、Customer_objtyp
オブジェクト型の型本体でcompareCustOrders
メソッドを定義します。
例A-23 Customer_objtyp型本体の作成
CREATE OR REPLACE TYPE BODY Customer_objtyp AS ORDER MEMBER FUNCTION compareCustOrders (x IN Customer_objtyp) RETURN INTEGER IS BEGIN RETURN CustNo - x.CustNo; END; END; /
前述のように、オーダー・メソッドcompareCustOrders
の操作では、2つの顧客発注に関する情報が比較されます。このメソッドは入力引数としてCustomer_objtyp
オブジェクトをもう1つ取り、2つのCustNo
番号の違いを戻します。戻り値は次のとおりです。
固有のオブジェクトのCustNo
値の方が小さい場合は負の数。
固有のオブジェクトのCustNo
値の方が大きい場合は正の数。
2つのオブジェクトのCustNo
値が同一の場合は0 (ゼロ)。この場合は、両方の発注が同じ顧客に関連付けられています。
戻り値が正、負または0(ゼロ)のいずれであるかは、顧客番号の相対順序を示します。たとえば、通常、小さい番号は大きい番号よりも前に作成されます。ORDER
メソッドの入力引数(SELF
と明示的引数)がNULL
の場合、ORDER
メソッドはコールされず、結果は単にNULL
として処理されます。
これで、発注書スキーマのオブジェクト・リレーショナル・バージョンについて、すべてのオブジェクト型を定義したことになります。実際の発注書データを含めるこれらの型のインスタンスは作成しておらず、この種のデータを格納する表も作成していません。次の項では、この作成方法について説明します。
オブジェクト型の作成は、表の作成とは異なります。型を作成する場合は、論理構造を定義するのみで、記憶域は作成しません。データへのオブジェクト・リレーショナル・インタフェースを使用するには、データをオブジェクト表に格納するか、リレーショナル表に残しておいてオブジェクト・ビューからアクセスするかに関係なく、オブジェクト型を作成する必要があります。オブジェクト・ビューとオブジェクト表は、どちらもオブジェクト型を前提とします。オブジェクト表またはオブジェクト・ビューは、常に特定のオブジェクト型の表またはビューです。この点では、常に指定のデータ型を持つリレーショナル列に似ています。
リレーショナル列と同様に、オブジェクト表に含めることができるのは、表と同一の宣言された型のオブジェクト・インスタンスなど、1種類の行のみです。(また、表が代入可能な場合は、宣言された型のサブタイプのインスタンスも含めることができます。)
オブジェクト表の各行は1つのオブジェクト・インスタンスです。そのため、ある意味では、オブジェクト表は宣言されたオブジェクト型の1列のみで構成されることになります。ただし、これはリレーショナル表の場合と異なるわけではありません。リレーショナル表の各行も、理論上は1つのエンティティ(リレーショナルCustomers
表内の顧客など)を表します。リレーショナル表の各列には、このエンティティの属性データが格納されます。
同様に、オブジェクト表では、オブジェクト型の属性は挿入および選択可能な列にマップされます。主な違いは、オブジェクト表の場合、データが表の型で定義された構造で格納される(および取出し可能である)ため、きわめて単純な問合せでデータのマルチレベル構造全体を取り出すことができるという点にあります。
次の文では、Customer_objtyp
型のオブジェクトを保持するオブジェクト表Customer_objtab
を定義します。
例A-24 Customer_objtab表の作成
CREATE TABLE Customer_objtab OF Customer_objtyp (CustNo PRIMARY KEY) OBJECT IDENTIFIER IS PRIMARY KEY;
リレーショナル表とは異なり、オブジェクト表の作成時には、データ型(格納されるオブジェクトの型)を指定します。
表には、次のようにCustomer_objtyp
の属性ごとに1列があります。
CustNo NUMBER /* Primary key */
CustName VARCHAR2(200)
Address_obj Address_objtyp
PhoneList_var PhoneList_vartyp
これらの型の定義は、例A-18「Address_objtypオブジェクトの作成」および例A-17「PhoneList_vartyp型の作成」を参照してください。
Customer_objtyp
型があるため、同じ型の多数のオブジェクト表を作成できます。たとえば、同じCustomer_objtyp
型のオブジェクト表Customer_objtab2
を作成できます。
複数の表を作成する場合は、バリエーションを導入できます。Customer_objtab
の作成に使用した文では、CustNo
列に主キー制約を定義しました。この制約は、このオブジェクト表にのみ適用されます。同じ型の別のオブジェクト表には、この制約がない場合があります。
Customer_objtab
には、行オブジェクトとして表される顧客オブジェクトが含まれています。Oracleでは、行オブジェクトを参照可能にでき、これは、他の行オブジェクトまたはリレーショナル行からオブジェクト識別子(OID)を使用して参照できることを意味します。たとえば、発注書行オブジェクトでは、オブジェクト参照を使用して顧客行オブジェクトを参照できます。オブジェクト参照は、REF
型で表されるシステム生成値であり、行オブジェクトの一意OIDに基づきます。
Oracleでは、各行オブジェクトに一意OIDが必要です。一意OID値をシステム生成として指定するか、行オブジェクトの主キーを一意OIDとして機能するように指定できます。この指定を行うには、CREATE
TABLE
文の実行時にOBJECT
IDENTIFIER
IS
PRIMARY
KEY
またはOBJECT
IDENTIFIER
IS
SYSTEM
GENERATED
を指定します。後者がデフォルトです。主キー値がデフォルトである16バイトのシステム生成識別子より小さい場合は、オブジェクト識別子として主キーを使用する方が効率的です。この例では、行オブジェクト識別子として主キーを使用しています。
Customer_objtab
のAddress_obj
列にAddress_objtyp
オブジェクトが含まれていることに注意してください。この図が示すように、オブジェクト型は、それ自体がオブジェクト型である属性を持つことができます。オブジェクト表について宣言された型のオブジェクト・インスタンスが行オブジェクトと呼ばれるのは、1つのオブジェクト・インスタンスが表の行全体を占めるためです。ただし、Address_obj
列にあるような埋込みオブジェクトは、列オブジェクトと呼ばれます。この種のオブジェクトは、行全体を占めないという点が行オブジェクトとは異なります。そのため参照不可であり、REF
のターゲットにすることはできません。また、NULL
にすることができます。
Address_objtyp
オブジェクトの属性は組込み型です。複合型ではなくスカラーである(つまり、固有の属性を持つオブジェクト型ではない)ため、ブランチの終了を表すことを反映するためにリーフ・レベル属性と呼ばれます。Address_objtyp
オブジェクトとその属性の列は、オブジェクト表Customer_objtab
に作成されます。これらの列の参照またはナビゲートには、ドット表記法を使用できます。たとえば、Zip
列に索引を作成する場合は、Address
.Zip
として参照できます。
PhoneList_var
列には、PhoneList_vartyp
型のVARRAYが含まれています。PhoneList_vartyp
型の各オブジェクトは、それぞれがVARCHAR2
型のデータ項目で表される最大10個の電話番号のVARRAYとして定義してあります。例A-17を参照してください。
PhoneList_vartyp
型の各VARRAYには最大200文字(10x20)と少量のオーバーヘッドを格納できるため、OracleではVARRAYがPhoneList_var
列に1つのデータ単位として格納されます。4000バイト以内のVARRAYはインラインのBLOB
に格納され、これは、VARRAY値の一部が表の外部に格納される可能性があることを意味します。
次の文では、StockItem_objtyp
オブジェクトのオブジェクト表を作成します。
例A-25 Stock_objtab表の作成
CREATE TABLE Stock_objtab OF StockItem_objtyp (StockNo PRIMARY KEY) OBJECT IDENTIFIER IS PRIMARY KEY;
この表の各行は、次の3つの数値属性を持つStockItem_objtyp
オブジェクトです。
StockNo NUMBER
Price NUMBER
TaxRate NUMBER
Oracleでは、属性ごとに1列が作成されます。CREATE
TABLE
文では、StockNo
列に主キー制約が適用され、主キーを行オブジェクトの識別子として使用するように指定されます。
次の文では、PurchaseOrder_objtyp
オブジェクトのオブジェクト表を定義します。
例A-26 PurchaseOrder_objtab表の作成
CREATE TABLE PurchaseOrder_objtab OF PurchaseOrder_objtyp ( /* Line 1 */ PRIMARY KEY (PONo), /* Line 2 */ FOREIGN KEY (Cust_ref) REFERENCES Customer_objtab) /* Line 3 */ OBJECT IDENTIFIER IS PRIMARY KEY /* Line 4 */ NESTED TABLE LineItemList_ntab STORE AS PoLine_ntab ( /* Line 5 */ (PRIMARY KEY(NESTED_TABLE_ID, LineItemNo)) /* Line 6 */ ORGANIZATION INDEX COMPRESS) /* Line 7 */ RETURN AS LOCATOR /* Line 8 */ /
このCREATE
TABLE
文では、PurchaseOrder_objtab
オブジェクト表が作成されます。各行の内容は次のとおりです。
行1:
CREATE
TABLE
PurchaseOrder_objtab
OF
PurchaseOrder_objtyp
(
この行は、表の各行がPurchaseOrder_objtyp
オブジェクトであることを示します。PurchaseOrder_objtyp
オブジェクトの属性は次のとおりです。
PONo NUMBER
Cust_ref REF Customer_objtyp
OrderDate DATE
ShipDate DATE
LineItemList_ntab LineItemList_ntabtyp
ShipToAddr_obj Address_objtyp
これらの型の定義は、例A-19「Customer_objtypオブジェクトの作成」および例A-20「LineItemList_ntabtyp型の作成」を参照してください。
行2:
PRIMARY
KEY
(PONo),
この行は、PONo
属性を表の主キーとして指定します。
行3:
FOREIGN
KEY
(Cust_ref)
REFERENCES
Customer_objtab)
この行は、Cust_ref
列の参照制約を指定します。この参照制約は、リレーショナル表に対して指定される制約と同様です。制約がない場合、REF
列では任意の行オブジェクトを参照できます。ただし、この場合、Cust_ref
REF
で参照できるのはCustomer_objtab
オブジェクト表の行オブジェクトのみです。
行4:
OBJECT
IDENTIFIER
IS
PRIMARY
KEY
この行は、PurchaseOrder_objtab
オブジェクト表の主キーが行のOIDとして使用されることを示します。
行5から8:
NESTED
TABLE
LineItemList_ntab
STORE
AS
PoLine_ntab
(
(PRIMARY KEY(NESTED_TABLE_ID, LineItemNo))
ORGANIZATION INDEX COMPRESS)
RETURN AS LOCATOR
この4行は、ネストした表の列LineItemList_ntab
の記憶域の指定とプロパティに関係しています。ネストした表の行は、別の記憶表に格納されます。この記憶表をユーザーが直接問い合せることはできませんが、メンテナンスのためにDDL文で参照することはできます。記憶表の非表示列NESTED_TABLE_ID
では、各行が対応する親行と一致します。特定の親に属するネストした表の全要素は同じNESTED_TABLE_ID
値を持ちます。たとえば、PurchaseOrder_objtab
の特定の行のネストした表の要素は、すべて同じNESTED_TABLE_ID
値を持ちます。PurchaseOrder_objtab
の別の行に属するネストした表の要素は、異なるNESTED_TABLE_ID
値を持ちます。
前述のCREATE
TABLE
の例では、行5はネストした表LineItemList_ntab
の行が別の表(記憶表)PoLine_ntab
に格納されることを示します。STORE
AS
句で記憶表に対する制約と記憶域を指定することもできます。この例で、行7は記憶表が索引構成表(IOT
)であることを示します。通常、ネストした表の行をIOTに格納すると、同じ親に属している行がクラスタ化されるというメリットがあります。IOT
にCOMPRESS
が指定されているため、記憶域が節約されますが、その理由は、COMPRESS
を指定しなければ、IOT
のキーのNESTED_TABLE_ID
部分が親の行オブジェクトの行ごとに繰り返されるためです。ただし、COMPRESS
を指定すると、NESTED_TABLE_ID
は親の行オブジェクトごとに一度のみ格納されます。
行6では、NESTED_TABLE_ID
およびLineItemNo
属性が2つの目的で記憶表の主キーとして指定されています。第1の目的はIOT
のキーを指定すること、第2の目的はネストした表のLineItemNo
列の一意性を親表の各行内で規定することです。この文では、LineItemNo
列をキーに含めることで、LineItemNo
列に各発注書内の個別値が確実に含まれるようになります。
行8は、ネストした表LineItemList_ntab
が取得時にロケータ形式で戻されることを示します。LOCATOR
を指定しなければ、デフォルトはVALUE
で、ロケータのみでなくネストした表全体が戻されます。ネストした表のコレクションに多数の要素が含まれている場合は、それらを含んでいる行オブジェクトまたは列が選択されたときに、ネストされた表全体を戻すのは非効率的です。
ネストした表のロケータが戻されるように指定すると、Oracleでは実際のコレクション値へのロケータのみをクライアントに送信できます。アプリケーションでは、OCICollIsLocator
またはUTL_COLL
.IS_LOCATOR
インタフェースをコールして、フェッチされたネストした表がロケータ形式であるか値形式であるかを検出できます。ロケータが戻されることを確認した後、アプリケーションでは問合せにロケータを使用して、ネストした表の行要素のうち必要なサブセットのみをフェッチできます。このような、ネストした表の行のロケータ・ベースの取得は、ネストした表の値またはコピー・セマンティクスを保つために、オリジナルの文のスナップショットに基づきます。つまり、ネストした表の行要素のサブセットをフェッチするためにロケータが使用される場合、ネストした表のスナップショットには、ロケータが最初に取得されたときのネストした表が反映されます。
ここで再び「メソッド定義」に示したPurchaseOrder_objtyp
のsumLineItems
メソッドの実装を考えてみます。その実装は、ネストした表LineItemList_ntab
がVALUE
として戻されることを前提としていました。大きいネストした表を効率的に処理し、PurchaseOrder_objtab
のネストした表がロケータとして戻されるという事実を活用するには、sumLineItems
メソッドを次のように書き換える必要があります。
例A-27 PurchaseOrder_objtyp型本体の置換
CREATE OR REPLACE TYPE BODY PurchaseOrder_objtyp AS MAP MEMBER FUNCTION getPONo RETURN NUMBER is BEGIN RETURN PONo; END; MEMBER FUNCTION sumLineItems RETURN NUMBER IS i INTEGER; StockVal StockItem_objtyp; Total NUMBER := 0; BEGIN IF (UTL_COLL.IS_LOCATOR(LineItemList_ntab)) -- check for locator THEN SELECT SUM(L.Quantity * L.Stock_ref.Price) INTO Total FROM TABLE(CAST(LineItemList_ntab AS LineItemList_ntabtyp)) L; ELSE FOR i in 1..SELF.LineItemList_ntab.COUNT LOOP UTL_REF.SELECT_OBJECT(LineItemList_ntab(i).Stock_ref,StockVal); Total := Total + SELF.LineItemList_ntab(i).Quantity * StockVal.Price; END LOOP; END IF; RETURN Total; END; END; /
書き換えたsumLineItems
メソッドは、UTL_COLL
.IS_LOCATOR
ファンクションを使用して、ネストした表の属性LineItemList_ntab
がロケータとして戻されるかどうかをチェックします。条件がTRUE
に評価されると、ネストした表のロケータはTABLE
式を使用して問合せされます。
注意: SQLコンパイル・エンジンにコレクション属性(またはパラメータまたは変数)の実際の型を指示して問合せをコンパイルできるように、現在、TABLE 式にはCAST 式が必要です。 |
ネストした表のロケータを問い合せる方が、発注書の大きい明細項目リストを効率的に処理できます。LineItemList_ntab
を反復する前述のコードは、ネストした表がVALUE
として戻される場合に対処するために残されています。
表の作成後に、ALTER
TABLE
文が発行され、REF
に対するSCOPE
FOR
制約が追加されます。REF
に対するSCOPE
FOR
制約は、CREATE
TABLE
文では指定できません。Stock_ref
で参照できるのがオブジェクト表Stock_objtab
のみであることを指定するには、PoLine_ntab
記憶表に対して次のALTER
TABLE
文を発行します。
この文では、ネストした表のStock_ref
列の有効範囲をStock_objtab
に制限するように指定します。これは、この列に格納される値がStock_objtab
内の行オブジェクトへの参照である必要があることを示します。参照制約とは異なり、SCOPE
制約には参照先オブジェクトへの依存性がありません。たとえば、
Stock_objtab
内で参照されている行オブジェクトは、ネストした表のStock_ref
列で参照されていても削除できます。このような削除により、ネストした表内の対応する参照はDANGLING REF
となります。
Oracleでは、記憶表に対する参照制約の指定はサポートされていません。この場合は、REF
列に対するSCOPE
句を指定すると有効です。通常、REF
列に対して有効範囲または参照制約を指定すると、次のようなメリットがあります。
Oracleで行オブジェクトの一意識別子のみを列にREF
値として格納できるため、記憶域の節約になります。
記憶表のREF
列に索引を作成できます。
Oracleでは、これらのREF
の参照解除を含む問合せを、参照表が関係する結合としてリライトできます。
この時点で、発注書アプリケーションのすべての表が作成されました。次の項では、これらの表の操作方法について説明します。
ここでは、前にリレーショナル表に挿入したのと同じデータをオブジェクト表に挿入する方法について説明します。型のインスタンスを作成するために、一部の値によりオブジェクト型のコンストラクタへのコールが取り込まれていることに注意してください。
例A-29 Stock_objtabへの値の挿入
INSERT INTO Stock_objtab VALUES(1004, 6750.00, 2) ; INSERT INTO Stock_objtab VALUES(1011, 4500.23, 2) ; INSERT INTO Stock_objtab VALUES(1534, 2234.00, 2) ; INSERT INTO Stock_objtab VALUES(1535, 3456.23, 2) ;
例A-30 Customer_objtabへの値の挿入
INSERT INTO Customer_objtab VALUES ( 1, 'Jean Nance', Address_objtyp('2 Avocet Drive', 'Redwood Shores', 'CA', '95054'), PhoneList_vartyp('415-555-0102') ) ; INSERT INTO Customer_objtab VALUES ( 2, 'John Nike', Address_objtyp('323 College Drive', 'Edison', 'NJ', '08820'), PhoneList_vartyp('609-555-0190','201-555-0140') ) ;
例A-31 PurchaseOrder_objtabへの値の挿入
INSERT INTO PurchaseOrder_objtab SELECT 1001, REF(C), SYSDATE, '10-MAY-1999', LineItemList_ntabtyp(), NULL FROM Customer_objtab C WHERE C.CustNo = 1 ;
この文では、次の属性を持つPurchaseOrder_objtyp
オブジェクトが構成されます。
PONo 1001
Cust_ref REF to customer number 1
OrderDate SYSDATE
ShipDate 10-MAY-1999
LineItemList_ntab an empty LineItem_ntabtyp
ShipToAddr_obj NULL
この文では、問合せを使用して、CustNo
値が1
であるCustomer_objtab
オブジェクト表の行オブジェクトへのREF
が構成されます。
次の文では、TABLE
式を使用して、ネストした表が挿入先として識別されます。これは、PONo
値が1001であるPurchaseOrder_objtab
表の行オブジェクトのLineItemList_ntab
列にあるネストした表です。
例A-32 LineItemList_ntabへの値の挿入
INSERT INTO TABLE ( SELECT P.LineItemList_ntab FROM PurchaseOrder_objtab P WHERE P.PONo = 1001 ) SELECT 01, REF(S), 12, 0 FROM Stock_objtab S WHERE S.StockNo = 1534 ;
この文では、TABLE
式で識別されたネストした表に明細項目が1つ挿入されます。挿入される明細項目には、Stock_objtab
オブジェクト表内でStockNo
値が1534
の行オブジェクトへのREF
が含まれています。
次の文は、前述の文と同じパターンに従います。
例A-33 PurchaseOrder_objtabおよびLineItemList_ntabへの値の挿入
INSERT INTO PurchaseOrder_objtab SELECT 2001, REF(C), SYSDATE, '20-MAY-1997', LineItemList_ntabtyp(), Address_objtyp('55 Madison Ave','Madison','WI','53715') FROM Customer_objtab C WHERE C.CustNo = 2 ; INSERT INTO TABLE ( SELECT P.LineItemList_ntab FROM PurchaseOrder_objtab P WHERE P.PONo = 1001 ) SELECT 02, REF(S), 10, 10 FROM Stock_objtab S WHERE S.StockNo = 1535 ; INSERT INTO TABLE ( SELECT P.LineItemList_ntab FROM PurchaseOrder_objtab P WHERE P.PONo = 2001 ) SELECT 10, REF(S), 1, 0 FROM Stock_objtab S WHERE S.StockNo = 1004 ; INSERT INTO TABLE ( SELECT P.LineItemList_ntab FROM PurchaseOrder_objtab P WHERE P.PONo = 2001 ) VALUES(11, (SELECT REF(S) FROM Stock_objtab S WHERE S.StockNo = 1011), 2, 1) ;
次の問合せ文は、暗黙的に比較メソッドを起動します。この例は、OracleでPurchaseOrder_objtyp
型のオブジェクトがその型の比較メソッドを使用して順序付けされる方法を示しています。
Oracleは、選択されたPurchaseOrder_objtyp
オブジェクトごとにマップ・メソッドgetPONo
を起動します。このメソッドはオブジェクトのPONo
属性を戻すため、選択により発注書番号の昇順リストが生成されます。
次の問合せは、リレーショナル・モデルに基づいて実行された問合せに対応します。
例A-35 発注書1001の顧客および明細項目データの問合せ
SELECT DEREF(p.Cust_ref), p.ShipToAddr_obj, p.PONo, p.OrderDate, LineItemList_ntab FROM PurchaseOrder_objtab p WHERE p.PONo = 1001 ;
例A-37 在庫品目1004に関する発注書および明細項目データの問合せ
SELECT po.PONo, po.Cust_ref.CustNo, CURSOR ( SELECT * FROM TABLE (po.LineItemList_ntab) L WHERE L.Stock_ref.StockNo = 1004 ) FROM PurchaseOrder_objtab po ;
前述の問合せでは、ネストした表から選択されたLineItem_obj
オブジェクトのセットについてネストしたカーソルが戻されます。アプリケーションでは、ネストしたカーソルからフェッチして個別のLineItem_obj
オブジェクトを取得できます。この問合せは、ネストしたセットを外側の結果からネスト解除して表すこともできます。
SELECT po.PONo, po.Cust_ref.CustNo, L.* FROM PurchaseOrder_objtab po, TABLE (po.LineItemList_ntab) L WHERE L.Stock_ref.StockNo = 1004 ;
前述の問合せでは、結果セットが平滑化された形式(第1標準形式)で戻されます。このタイプの問合せは、リレーショナル・ツールやODBCなどのAPIからOracleコレクション列にアクセスする際に役立ちます。前述のネスト解除例では、LineItemList_ntab
行を持つPurchaseOrder_objtab
オブジェクト表の行のみが戻されます。対応するLineItemList_ntab
内の行の有無に関係なくPurchaseOrder_objtab
表の行をすべてフェッチするには、(+)演算子が必要です。
SELECT po.PONo, po.Cust_ref.CustNo, L.* FROM PurchaseOrder_objtab po, TABLE (po.LineItemList_ntab) (+) L WHERE L.Stock_ref.StockNo = 1004 ;
例A-38の要求では、すべてのPurchaseOrder_objtab
行のすべてのネストした表LineItemList_ntab
の行を問い合せる必要があります。ネスト解除も必要です。
完成して完全にビルドされたアプリケーションであっても、未完成となる傾向があります。要件が変化したり、基礎となるオブジェクト・モデルやスキーマを新しい状況にあわせて変更せざるを得なくなったり、当初意図していたものよりも適切にジョブを実行するためにオブジェクト・モデルを改良する方法があったりする場合があります。
オブジェクト・リレーショナル・アプリケーションの運用開始後しばらくして、設計を改良する方法が明らかになったとします。特に、ユーザーは顧客レコードを表示するときに、ほぼ常に購入履歴を確認しようとすることが判明したとします。これを現行のオブジェクト・モデルで実行するには、顧客情報と発注書情報が保持される2つの表Customer_objtab
およびPurchaseOrder_objtab
の結合が必要です。そこで、関連発注書に関するデータに顧客表から直接アクセスできるように設計を改良することになったとします。
その方法の1つは、顧客の発注書情報がその顧客を表すオブジェクト・インスタンスに適切に組み込まれるように、Customer_objtyp
を変更することです。つまり、発注書情報の属性をCustomer_objtyp
に追加する必要があります。複数の発注書に関する情報を保持するには、属性をコレクション型(ネストした表)にする必要があります。
属性の追加は、オブジェクト型を変更または進化させる方法の1つです。型を進化させると、Oracleでは変更内容が型自体およびすべての依存スキーマ・オブジェクト(型のサブタイプ、変更された型を属性として持つ他のオブジェクト型および変更された型の表と列)に適用されます。
Customer_objtyp
を変更して発注書のネストした表の属性を追加するには、次のように複数の手順が必要です。
発注書のネストした表に使用する新規の型を作成します。
Customer_objtyp
を変更して新規の型の新規の属性を追加します。
Customer_objtab
オブジェクト表で、新規に追加したネストした表の記憶表の名前と有効範囲を指定します。
新規の属性にあわせてCustomer_objtab
オブジェクト表をアップグレードすると、発注書自体に明細項目のネストした表が含まれるため、一方の内側に他方が含まれる2レベルのネストした表が実際に追加されます。
発注書のネストした表と明細項目のネストした表の両方に主キー・ベースのREF
を使用できるように、両方について有効範囲を指定する必要があります。この詳細は、次の項を参照してください。
前述の手順を完了すると、モデルにおける顧客情報と発注書情報の論理的な関連性が明確になり、顧客、発注書および明細項目に関するすべての情報について顧客表を問合せできるようになります。また、顧客表に対する1つのINSERT
文で新規顧客の新規発注書を挿入できます。
発注書のネストした表をCustomer_objtyp
の属性として追加する前に、この種のネストした表の型を定義する必要があります。そのためには次の文を使用します。
例A-40 PurchaseOrderList_ntabtypの作成
CREATE TYPE PurchaseOrderList_ntabtyp AS TABLE OF PurchaseOrder_objtyp; /
これで、ALTER
TYPE
文を使用して、この型の属性をCustomer_objtyp
に追加できます。
例A-41 Customer_objtypの変更
ALTER TYPE Customer_objtyp ADD ATTRIBUTE (PurchaseOrderList_ntab PurchaseOrderList_ntabtyp) CASCADE;
変更する型に依存型または依存表がある場合は、型に対するALTER
TYPE
文でCASCADE
またはINVALIDATE
を指定して、依存オブジェクトに対する変更内容の適用方法を指定する必要があります。
CASCADE
を指定すると、型変更の適用前に依存オブジェクトの妥当性チェックが実行されます。これらのチェックでは、変更により、表のパーティション・キーとして使用中の属性が削除されるなどの不正動作が発生しないことが確認されます。依存オブジェクトが検証に失敗すると、型の変更は異常終了します。これに対して、すべての依存オブジェクトが検証に成功すると、システムは型の変更を伝播するために必要なメタデータおよびデータの変更に進みます。これには、列の追加と削除、ネストした表の記憶表の作成などが自動的に含まれる場合があります。
INVALIDATE
オプションを指定すると、予備的な妥当性チェックはスキップされ、型の変更が依存オブジェクトに直接適用されます。それらのオブジェクトは、次回のアクセス時に検証されます。このようにして型を変更すると検証所要時間が短縮されますが、後にユーザーがアクセスを試行した依存表を検証できない場合は、表が検証に合格するまでそのデータにアクセスできなくなります。
Customer_objtab
表に追加される発注書と明細項目の新規のネストした表ごとに、REF
列の有効範囲を追加する必要があります。便宜上、まず新規の表の名前をシステム生成名から認識可能な名前に変更します。次に、指定した名前を使用して記憶表を変更し、そのREF
列の有効範囲を追加できます。
この操作を実行する必要があるのは、主キー・ベースのオブジェクト識別子を持つ表の列にオブジェクトへのREF
を格納するには、列の有効範囲としてその表を指定するか、参照制約を適用する必要があるためです。列の有効範囲を特定の表に制限するには、その列のすべてのREF
がその表のオブジェクトへのREF
であると宣言します。この宣言が必要となるのは、主キー・ベースのオブジェクト識別子の一意性が保証されるのが特定の表のコンテキスト内のみであり、すべての表にまたがって一意であるとはかぎらないためです。主キー・ベースのREF
またはユーザー定義REF
を有効範囲なしの列に挿入しようとすると、次のようなエラーが戻されます。
オブジェクト・ビューREFまたはユーザー定義REFを挿入できません。
明細項目には、オブジェクト識別子に表の主キーが使用されているStock_objtab
表内のオブジェクトへのREF
が含まれます。このため、PurchaseOrder_objtab
表を作成した後で、その表内の明細項目のネストした表の記憶表にREF
列の有効範囲を追加する必要がありました。次に、これと同じ操作を表Customer_objtab
内の明細項目の新規のネストした表について実行する必要があります。
さらに、Customer_objtab
表に追加する発注書の新規のネストした表について、同じ操作を実行する必要があります。発注書ではCustomer_objtab
表の顧客が参照され、この表のオブジェクト識別子も主キー・ベースです。
次の文を使用して、名前を変更できるようにシステム生成表の名前を確認します。
SELECT table_name, parent_table_name, parent_table_column FROM user_nested_tables;
出力は次のようになります。
TABLE_NAME PARENT_TABLE_NAME PARENT_TABLE_COLUMN ----------------------------- ----------------------------- ---------------------- SYSNTQOFArJyBTHu6iOMMKU4wHw== CUSTOMER_OBJTAB PURCHASEORDERLIST_NTAB POLINE_NTAB PURCHASEORDER_OBJTAB LINEITEMLIST_NTAB SYSNTZqu6IQItR++UAtgz1rMB8A== SYSNTQOFArJyBTHu6iOMMKU4wHw== LINEITEMLIST_NTAB
便宜上、システム生成のネストした表を適切な名前に変更します。たとえば、前述のサンプル出力のシステム生成名を使用して、次のように変更します。
ALTER TABLE "SYSNTQOFArJyBTHu6iOMMKU4wHw==" RENAME TO PO_List_nt; ALTER TABLE "SYSNTZqu6IQItR++UAtgz1rMB8A==" RENAME TO Items_List_nt;
システム生成のネストした表の名前を変更するプロセスは、次のPL/SQLプロシージャを使用して自動的に実行することもできます。
DECLARE nested_table_1 VARCHAR2(30); nested_table_2 VARCHAR2(30); cust_obj_table VARCHAR2(30) := 'CUSTOMER_OBJTAB'; BEGIN EXECUTE IMMEDIATE ' SELECT table_name FROM user_nested_tables WHERE parent_table_name = :1 ' INTO nested_table_1 USING cust_obj_table; EXECUTE IMMEDIATE ' SELECT table_name FROM user_nested_tables WHERE parent_table_name = :1 ' INTO nested_table_2 USING nested_table_1; EXECUTE IMMEDIATE 'ALTER table "'|| nested_table_1 ||'" RENAME TO PO_List_nt'; EXECUTE IMMEDIATE 'ALTER table "'|| nested_table_2 ||'" RENAME TO Items_List_nt'; END; /
新規の記憶表の名前はPO_List_nt
およびItems_List_nt
です。次の文では、この2つの表のREF
列の有効範囲を特定の表に限定します。
例A-42 ネストした表へのREFの有効範囲の追加
ALTER TABLE PO_List_nt ADD (SCOPE FOR (Cust_Ref) IS Customer_objtab); ALTER TABLE Items_List_nt ADD (SCOPE FOR (Stock_ref) IS Stock_objtab);
Customer_objtab
に顧客の発注書を挿入する前に、もう1つ必要な操作があります。PurchaseOrderList_ntabtyp
の実際のネストした表を、表内の顧客ごとにインスタンス化する必要があります。
新規の属性に関する列が表に追加されると、既存の行の列値がNULL
に初期化されます。これは、既存の各顧客のネストした発注書表がアトミックNULL
に設定されること(そこには実際にネストした表は存在せず、空の表もないこと)を意味します。顧客ごとにネストした表をインスタンス化するまでは、発注書を挿入しようとすると次のようなエラーが戻されます。
NULLの表値を参照しています。
次の文では、実際のネストした表のインスタンスを含むように各行を更新して、発注書を保持する列を準備します。
例A-43 Customer_objtabの更新
UPDATE Customer_objtab c SET c.PurchaseOrderList_ntab = PurchaseOrderList_ntabtyp();
この文で、PurchaseOrderList_ntabtyp()
はネストした表の型のコンストラクタ・メソッドへのコールです。このコールでは、発注書は指定されておらず、空のネストした表が作成されます。
この時点までに、Customer_objtyp
型を進化させて発注書のネストした表を追加し、ネストした表に発注書を格納できるように表Customer_objtab
を設定しました。これで、発注書をCustomer_objtab
に挿入する準備が完了したことになります。
表PurchaseOrder_objtab
には、すでに2件の発注書があります。次の2つの文では、これらの発注書をCustomer_objtab
にコピーします。
例A-44 Customer_objtabへの発注書の挿入
INSERT INTO TABLE ( SELECT c.PurchaseOrderList_ntab FROM Customer_objtab c WHERE c.CustNo = 1 ) SELECT VALUE(p) FROM PurchaseOrder_objtab p WHERE p.Cust_Ref.CustNo = 1; INSERT INTO TABLE ( SELECT c.PurchaseOrderList_ntab FROM Customer_objtab c WHERE c.CustNo = 2 ) SELECT VALUE(p) FROM PurchaseOrder_objtab p WHERE p.Cust_Ref.CustNo = 2;
前述の各INSERT
文には2つの主要部分があります。挿入操作のターゲット表を指定するTABLE
式と、挿入するデータを取得するSELECT
です。各部分のWHERE
句では、発注書を(TABLE
式で)受け取る顧客オブジェクトと、発注書の(発注書を取得する副問合せでの)選択対象となる顧客を選択します。
副問合せのWHERE
句では、ドット表記法を使用してCustNo
属性p.Cust_Ref.CustNo
にナビゲートします。ドット表記法を使用する場合は、表の別名p
が必須であることに注意してください。別名を省略してかわりにCust_Ref.CustNo
を指定すると、エラーになります。
このWHERE
句のドット表記法については、発注書のCust_Ref
REF
属性を介して顧客のCustNo
属性にナビゲートできることにも注意する必要があります。PL/SQLではなくSQLは、この方法でドット表記法に使用されているREF
を暗黙的に参照解除します。
INSERT
文の最初の部分のTABLE
式は、式から戻されるコレクションを表として処理するようにシステムに対して指示します。ここでは、式は特定の顧客に関する発注書のネストした表を挿入ターゲットとして選択するために使用されています。
INSERT
文の第2の部分では、VALUE()
ファンクションが選択した行をオブジェクトとして戻します。この場合、各行は発注書オブジェクトであり、明細項目の固有のコレクションで終了します。発注書の各行は、PurchaseOrder_objtyp
型のある表から選択され、その型の別の表に挿入されます。
前述のINSERT
文では、PurchaseOrder_objtyp
の顧客参照属性を使用して、既存の各発注書が属している顧客を識別しています。ただし、この時点で、古い発注書がすべて発注書表からアップグレード後のCustomer_objtab
にコピーされ、発注書のこの顧客参照属性は使用されなくなります。これで、発注書は顧客オブジェクト自体に格納されるようになります。
次のALTER
TYPE
文では、顧客参照属性を削除するためにPurchaseOrder_objtyp
を進化させます。この文では、出荷先所在地が常に顧客所在地と同一であるとみなし、ShipToAddr_obj
属性も冗長として削除します。
例A-45 PurchaseOrder_objtypの変更
ALTER TYPE PurchaseOrder_objtyp DROP ATTRIBUTE Cust_ref, DROP ATTRIBUTE ShipToAddr_obj CASCADE;
この時点で、CASCADE
オプションを使用して、システムで検証を実行し、依存型および依存表に対して必要な変更をすべて行えるようになりました。
前述したINSERT
の例は、VALUE()
ファンクションを使用して、既存の発注書オブジェクトを選択し、発注書のネストした表に挿入して、明細項目の固有のネストした表で完了する方法を示していました。次の例は、発注書オブジェクトとしてインスタンス化されていない新規発注書の挿入方法を示しています。この場合、発注書の明細項目のネストした表を、各明細項目オブジェクトおよびデータとともにインスタンス化する必要があります。参照しやすいように明細番号を左側に示してあります。
例A-46 VALUE()を使用したLineItemList_ntabtypへの挿入
INSERT INTO TABLE ( /* Line 1 */ SELECT c.PurchaseOrderList_ntab /* Line 2 */ FROM Customer_objtab c /* Line 3 */ WHERE c.CustName = 'John Nike' /* Line 4 */ ) /* Line 5 */ VALUES (1020, SYSDATE, SYSDATE + 1, /* Line 6 */ LineItemList_ntabtyp( /* Line 7 */ LineItem_objtyp(1, MAKE_REF(Stock_objtab, 1004), 1, 0), /* Line 8 */ LineItem_objtyp(2, MAKE_REF(Stock_objtab, 1011), 3, 5), /* Line 9 */ LineItem_objtyp(3, MAKE_REF(Stock_objtab, 1535), 2, 10) /* Line 10 */ ) /* Line 11 */ ); /* Line 12 */
行1から5では、TABLE
式を使用して、挿入対象となるネストした表(顧客John Nikeに関する発注書のネストした表など)を選択します。
VALUES
句(行6から12)には、次のように新規発注書の各属性の値が含まれています。
PONo
OrderDate
ShipDate
LineItemList_ntab
INSERT
文の行6では、3つの発注書属性PONo
、OrderDate
およびShipDate
の値を指定します。
属性値のみが指定され、発注書コンストラクタは指定されていません。ネストした表は発注書のネストした表として宣言されているため、発注書コンストラクタを明示的に指定して発注書インスタンスをネストした表内でインスタンス化する必要はありません。発注書コンストラクタを省略すると、システムにより自動的に発注書がインスタンス化されます。ただし、必要な場合はコンストラクタを指定でき、その場合、VALUES
句は次のようになります。
INSERT INTO TABLE ( SELECT c.PurchaseOrderList_ntab FROM Customer_objtab c WHERE c.CustName = 'John Nike' ) VALUES ( PurchaseOrder_objtyp(1025, SYSDATE, SYSDATE + 1, LineItemList_ntabtyp( LineItem_objtyp(1, MAKE_REF(Stock_objtab, 1004), 1, 0), LineItem_objtyp(2, MAKE_REF(Stock_objtab, 1011), 3, 5), LineItem_objtyp(3, MAKE_REF(Stock_objtab, 1535), 2, 10) ) ) )
行7から11では、明細項目のネストした表をインスタンス化してデータを提供します。コンストラクタ・メソッドLineItemList_ntabtyp(…)
では、3つの明細項目を含むネストした表のインスタンスを作成します。
明細項目コンストラクタLineItem_objtyp()
では、各明細項目のオブジェクト・インスタンスを作成します。明細項目属性の値は、コンストラクタの引数として指定します。
MAKE_REF
ファンクションでは、明細項目のStock_ref
属性のREF
を作成します。MAKE_REF
の引数は、在庫表の名前と、参照する在庫品目の主キー値です。ここでMAKE_REF
を使用できるのは、在庫表内のオブジェクト識別子が主キーに基づいているためです。主キーに基づいていない場合は、副問合せでREF
ファンクションを使用して在庫表の行へのREF
を取得する必要があります。
他の(埋込みではなく)トップレベルの列または属性のように、名前をSELECT
構文のリストに指定すると、トップレベルのネストした表列を問い合せることができますが、結果はあまり読みやすくありません。たとえば、次の問合せでは、John Nikeに関する発注書のネストした表を選択します。
例A-47 顧客John Nikeに関するCustomer_objtabの問合せ
SELECT c.PurchaseOrderList_ntab FROM Customer_objtab c WHERE CustName = 'John Nike';
この問合せでは、次のような結果が生成されます。
PURCHASEORDERLIST_NTAB(PONO, ORDERDATE, SHIPDATE, LINEITEMLIST_NTAB(LINEITEMNO, -------------------------------------------------------------------------------- PURCHASEORDERLIST_NTABTYP(PURCHASEORDER_OBJTYP(2001, '25-SEP-01', '20-MAY-97', L INEITEMLIST_NTABTYP(LINEITEM_OBJTYP(10, 00004A038A00468ED552CE6A5803ACE034080020 B8C8340000001426010001000100290000000000090600812A00078401FE0000000B03C20B050000 ...
インスタンス・データをネストしていない形式で表示し、REF
をまったく表示しない方が読みやすくなります。TABLE
式を(この場合は問合せのFROM
句に)使用すると、読みやすい形式で表示できます。
たとえば、例A-48の問合せでは、John Nikeに属している全発注書の発注書番号、発注日および出荷日を選択します。
例A-48 TABLE式を使用したCustomer_objtabの問合せ
SELECT p.PONo, p.OrderDate, p.Shipdate FROM Customer_objtab c, TABLE(c.PurchaseOrderList_ntab) p WHERE c.CustName = 'John Nike';
PONO ORDERDATE SHIPDATE
------- --------- ---------
2001 25-SEP-01 26-SEP-01
1020 25-SEP-01 26-SEP-01
TABLE
式は引数としてコレクションを取り、SQL文のSQL表と同様に使用できます。前述の問合せでは、FROM
句のTABLE
式に発注書のネストした表をリストすると、通常の表列の場合と同様にネストした表の列を選択できます。各列は、使用する表別名p
により、ネストした表に属する列として識別されます。この例に示すとおり、FROM
句のTABLE
式にも、式自身の表別名を持たせることが可能です。
TABLE
式では、ネストした表は顧客表Customer_objtab
自体の表別名c
により、顧客表の列として識別されます。FROM
句では、Customer_objtab
表がそれを参照するTABLE
式の前に指定されていることに注意してください。このように、TABLE
式でFROM
句の左辺に指定されている表別名を使用する機能を、左相関といいます。これを使用すると、表とTABLE
式をデイジー・チェーン化できます(これには、別のTABLE
式の表別名を使用するTABLE
式が含まれます)。実際には、この方法を使用して、他のネストした表に埋め込まれているネストした表の列を選択できます。
たとえば、次の例に発注書番号1020に関するすべての明細項目情報を選択する問合せを示します。
例A-49 発注書1020に関するCustomer_objtabの問合せ
SELECT p.PONo, i.LineItemNo, i.Stock_ref.StockNo, i.Quantity, i.Discount FROM Customer_objtab c, TABLE(c.PurchaseOrderList_ntab) p, TABLE(p.LineItemList_ntab) i WHERE p.PONo = 1020;
PONO LINEITEMNO STOCK_REF.STOCKNO QUANTITY DISCOUNT
----- ---------- ----------------- ---------- ----------
1020 1 1004 1 0
1020 2 1011 3 5
1020 3 1535 2 10
この問合せでは2つのTABLE
式を使用し、第2の式で第1の式を参照しています。明細項目情報は、外側のネストした表内の発注書番号1020に属している内側のネストした表から選択されます。
顧客表の列は、SELECT
構文のリストにもWHERE
句にも指定されていないことに注意してください。顧客表は、ネストした表へのアクセス開始点を指定するためにのみFROM
句に指定されています。
次に前述の問合せのバリエーションを示します。このバージョンは、ワイルド・カード*
を使用してTABLE
式のコレクションの列をすべて指定できることを示しています。
SELECT p.PONo, i.* FROM Customer_objtab c, TABLE(c.PurchaseOrderList_ntab) p, TABLE(p.LineItemList_ntab) i WHERE p.PONo = 1020;
会計マネージャを介して大口の標準顧客を多数処理するとします。会計マネージャのIDに使用するフィールドを、これらの顧客の顧客レコードに追加する必要があります。
前述の例では、発注書のネストした表の属性を追加する必要が生じたときに、顧客の型自体を進化させました。会計マネージャIDの属性を追加する際には、前回と同様の方法を使用するか、顧客型のサブタイプを作成し、サブタイプにのみ属性を追加できます。どちらの方法を使用するのが適切であるかを考えてみます。
この種の決定を行うには、提示された新規属性をベース型のすべてのインスタンス(すべての顧客)に有意かつ有効に適用できるか、またはベース型で識別可能なサブクラスにのみ適用できるかを検討する必要があります。
前述の例では、すべての顧客が発注書を持っているため、型自体を変更してその属性を追加する方法が適切でした。ただし、すべての顧客に会計マネージャがいるとはかぎらず、実際に会計マネージャがいるのは法人顧客のみです。そのため、顧客型を進化させて一般顧客には意味のない属性を追加するかわりに、識別した特殊な種類の顧客用に新規のサブタイプを作成し、そこに新規の属性を追加する方が適切です。
ベース型に基づいてサブタイプを作成できるのは、ベース型でサブタイプが許可されている場合のみです。型のサブタイプを作成できるかどうかは、その型のFINAL
プロパティに応じて異なります。デフォルトでは、新規の型はFINAL
として作成されます。これは、それがシリーズの最後の型であり、サブタイプを作成できないことを意味します。サブタイプを作成できる型を作成するには、顧客型を作成したときと同様にCREATE
TYPE
文でNOT
FINAL
を指定する必要があります。
サブタイプを定義するには、CREATE
TYPE
文でUNDER
キーワードを使用します。次の文では、Customer_objtyp
に新規のサブタイプCorp_Customer_objtyp
を作成します。この型はNOT
FINAL
として作成されるため、後で追加が必要になった場合にサブタイプを持たせることができます。
例A-50 Corp_Customer_objtypの作成
CREATE TYPE Corp_Customer_objtyp UNDER Customer_objtyp (account_mgr_id NUMBER(6) ) NOT FINAL; /
CREATE
TYPE
文を使用して新規サブタイプを作成する際には、新規に追加する属性およびメソッドのみを指定します。サブタイプはベース型から既存の属性とメソッドをすべて継承するため、指定する必要はありません。新規の属性およびメソッドは、継承された属性およびメソッドの後に追加されます。たとえば、新規Corp_Customer_objtyp
サブタイプの属性の完全リストは次のようになります。
CustNo
CustName
Address_obj
Phonelist_var
PurchaseOrderList_ntab
Account_mgr_id
デフォルトでは、サブタイプのインスタンスは、対応する任意のベース型の任意の列またはオブジェクト表に格納できます。このように、ベース型のスロットにサブタイプのインスタンスを格納する機能を、代入性といいます。列と表は、明示的にNOT
SUBSTITUTABLE
として宣言されていないかぎり代入可能です。サブタイプ属性用の新規列と、各行に格納されるインスタンスの型ID用の非表示列が、システムにより自動的に追加されます。
実際には、FINAL
型のサブタイプを作成できますが、最初にALTER
TYPE
文を使用して型をFINAL
型からNOT
FINAL
型に進化させておく必要があります。変更した型の既存の列および表に新規サブタイプのインスタンスを格納可能にするには、ALTER
TYPE
文でCASCADE
オプションCONVERT
TO
SUBSTITUTABLE
を指定します。「型進化」を参照してください。
列またはオブジェクト表が代入可能な場合は、その列または表の宣言された型のインスタンスのみでなく、宣言された型の任意のサブタイプのインスタンスも挿入できます。Customer_objtab
表の場合、これは表を使用してあらゆる種類(標準と法人の両方)の顧客に関する情報を格納できることを意味します。ただし、サブタイプの場合、情報の挿入方法には1つ重要な違いがあります。つまり、サブタイプのコンストラクタを明示的に指定する必要があります。コンストラクタの使用がオプションとなるのは、列または表の宣言された型のインスタンスの場合のみです。
たとえば、次の文では新規の標準顧客William Kiddを挿入します。
例A-51 標準顧客に関するデータの挿入
INSERT INTO Customer_objtab VALUES ( 3, 'William Kidd', Address_objtyp('43 Harbor Drive', 'Redwood Shores', 'CA', '95054'), PhoneList_vartyp('650-555-0188'), PurchaseOrderList_ntabtyp() );
VALUES
句には各Customer_objtyp
属性のデータが含まれていますが、Customer_objtyp
コンストラクタは省略されています。ここでコンストラクタがオプションなのは、表の宣言された型がCustomer_objtyp
であるためです。ネストした表の属性の場合、コンストラクタPurchaseOrderList_ntabtyp()
では空のネストした表が作成されますが、発注書のデータは指定されていません。
次の例に、同じ表に新規の法人顧客を挿入する文を示します。コンストラクタCorp_Customer_objtyp()
が使用され、会計マネージャIDのデータ値531
が指定されていることに注意してください。
例A-52 法人顧客に関するデータの挿入
INSERT INTO Customer_objtab VALUES ( Corp_Customer_objtyp( -- Subtype requires a constructor 4, 'Edward Teach', Address_objtyp('65 Marina Blvd', 'San Francisco', 'CA', '94777'), PhoneList_vartyp('415-555-0198', '415-555-0199'), PurchaseOrderList_ntabtyp(), 531 ) );
次の文では、2つの新規顧客それぞれについて1件の発注書を挿入します。新規顧客を挿入する文とは異なり、発注書を挿入する2つの文の構造は同じですが、発注書の明細項目数が異なっています。
例A-53 標準顧客に関する発注書の挿入
INSERT INTO TABLE ( SELECT c.PurchaseOrderList_ntab FROM Customer_objtab c WHERE c.CustName = 'William Kidd' ) VALUES (1021, SYSDATE, SYSDATE + 1, LineItemList_ntabtyp( LineItem_objtyp(1, MAKE_REF(Stock_objtab, 1535), 2, 10), LineItem_objtyp(2, MAKE_REF(Stock_objtab, 1534), 1, 0) ) );
例A-54 法人顧客に関する発注書の挿入
INSERT INTO TABLE ( SELECT c.PurchaseOrderList_ntab FROM Customer_objtab c WHERE c.CustName = 'Edward Teach' ) VALUES (1022, SYSDATE, SYSDATE + 1, LineItemList_ntabtyp( LineItem_objtyp(1, MAKE_REF(Stock_objtab, 1011), 1, 0), LineItem_objtyp(2, MAKE_REF(Stock_objtab, 1004), 3, 0), LineItem_objtyp(3, MAKE_REF(Stock_objtab, 1534), 2, 0) ) );
代入可能な列または表には、複数のデータ型のデータを含めることができます。これにより、たとえば顧客表の1回の問合せで全種類の顧客に関する情報を取得できます。ただし、特定の種類の顧客に関する情報のみ、またはその顧客の特定の属性に関する情報のみを取得することもできます。
次の例に、代入可能な表または列から必要な情報を取得する際に役立つ方法をいくつか示します。
例A-55に示す問合せでは、IS
OF
条件を含むWHERE
句を使用して、ある種の法人顧客でない顧客を除外します。つまり、この問合せでは、法人顧客はすべて戻されますが、他の種類の顧客のインスタンスは戻されません。
例A-55 すべての法人顧客とそのサブタイプの選択
SELECT c.* FROM Customer_objtab c WHERE VALUE(c) IS OF (Corp_Customer_objtyp);
例A-56に示す問合せは前述の例と似ていますが、Corp_Customer_objtyp
のサブタイプを除外するためにIS
OF
条件にONLY
キーワードが追加されています。最も限定的な型がCorp_Customer_objtyp
であるインスタンスの行のみが戻されます。
例A-56 サブタイプのないすべての法人顧客の選択
SELECT p.PONo FROM Customer_objtab c, TABLE(c.PurchaseOrderList_ntab) p WHERE VALUE(c) IS OF (ONLY Corp_Customer_objtyp);
例A-57に示す問合せでは、TABLE
式を使用して発注書番号を(発注書のネストした表から)取得します。全種類の顧客がこの属性を持っていますが、WHERE
句により検索対象が法人顧客のみに限定されています。
例A-57 法人顧客のみの発注書番号の選択
SELECT p.PONo FROM Customer_objtab c, TABLE(c.PurchaseOrderList_ntab) p WHERE VALUE(c) IS OF (Corp_Customer_objtyp);
例A-58に示す問合せでは、会計マネージャIDのデータが戻されます。これは法人顧客サブタイプでのみ保持される属性であり、表の宣言された型には欠落しています。問合せでは、サブタイプ属性Account_mgr_id
にアクセスするために、TREAT()
ファンクションを使用して各顧客を法人顧客として処理させています。
例A-58 TREATファンクションを使用したサブタイプ属性の選択
SELECT CustName, TREAT(VALUE(c) AS Corp_Customer_objtyp).Account_mgr_id FROM Customer_objtab c WHERE VALUE(c) IS OF (ONLY Corp_Customer_objtyp);
例A-58でTREAT()
が必要なのは、Account_mgr_id
が表の宣言された型Customer_objtyp
の属性ではないためです。前述のように属性をSELECT
構文のリストで指定するのみの場合、例A-59のような問合せでは「無効な列名」
というエラーが戻されます。これは、
Corp_Customer_objtyp
のインスタンスを除く全インスタンスを除外するWHERE
句を使用した場合も同じです。WHERE
句は結果から行を除外するのみであるため、ここでは不十分です。
例A-59 TREATファンクションを使用しないサブタイプ属性の選択
-- Following statement returns error, invalid column name for Account_mgr_id SELECT CustName, Account_mgr_id FROM Customer_objtab c WHERE VALUE(c) IS OF (ONLY Corp_Customer_objtyp);
代入可能な各列またはオブジェクト表には、各行のインスタンスの型を識別する非表示の型ID列が関連付けられています。型の型IDは、USER_TYPES
カタログ・ビューで確認できます。
ファンクションSYS_TYPEID()
は、特定のインスタンスの型IDを戻します。例A-60に示す問合せでは、USER_TYPES
カタログ・ビューでSYS_TYPEID()
および結合を使用して、表Customer_objtab
内の各顧客インスタンスの型名を戻します。
例A-60 各インスタンスの型の検出
SELECT c.CustName, u.TYPE_NAME FROM Customer_objtab c, USER_TYPES u WHERE SYS_TYPEID(VALUE(c)) = u.TYPEID;
--------------------------------- ---------------------
Jean Nance CUSTOMER_OBJTYP
John Nike CUSTOMER_OBJTYP
William Kidd CUSTOMER_OBJTYP
Edward Teach CORP_CUSTOMER_OBJTYP
SYS_TYPEID()
、VALUE()
およびTREAT()
の詳細は、「オブジェクトに便利なファンクションおよび演算子」を参照してください。