2.3 SQLオブジェクト型の継承
SQLオブジェクト継承は、型階層を構成するオブジェクト型の系図に基づいています。型の階層は、スーパータイプという親オブジェクト型と、親から導出された、サブタイプという1つ以上のレベルの子オブジェクト型で構成されます。
内容は次のとおりです。
2.3.1 SQLオブジェクト型の継承について
継承は、階層内のサブタイプを対応するスーパータイプに接続するメカニズムです。
サブタイプは、その親タイプの属性およびメソッドを自動的に継承します。また、継承リンクも保持されます。サブタイプは、親の属性またはメソッドの変更を自動的に取得します。スーパータイプの属性またはメソッドが更新されると、サブタイプにおいてもその更新が行われます。
注意:
単一継承のみがサポートされています。したがって、サブタイプは1つのスーパータイプからのみ直接導出でき、複数のスーパータイプから導出することはできません。
型階層内でオブジェクト型を使用すると、顧客などのエンティティをモデル化したり、元の型を基にして顧客の特化された様々なサブタイプを定義することができます。このように定義した後、階層に対して操作を行い、それぞれの型を実装させ、専用の方法で操作を実行できます。
2.3.2 スーパータイプおよびサブタイプ
サブタイプは、スーパータイプから直接または他のサブタイプを介して間接的に導出できます。
スーパータイプは、兄弟関係にあるサブタイプを複数持つことができますが、サブタイプが持つことができる直系の親スーパータイプは1つのみです(単一継承)。
スーパータイプからサブタイプを導出するには、親から継承したセットに新しい属性およびメソッドを追加するスーパータイプの特殊な異型を定義するか、継承したメソッドを再定義(オーバーライド)します。たとえば、person_typ
オブジェクト型からは、特殊な異型であるstudent_typ
やemployee_typ
を導出できます。これらのサブタイプは、根本的にはperson_typ
ですが、特殊な種類の個人です。サブタイプが親から受け取った属性またはメソッドに加えられたなんらかの変更が、サブタイプと親のスーパータイプを区別するものとなります。
継承したメソッドを再定義しない場合、サブタイプには常に親タイプが持つ属性およびメソッドの同じコア・セットと、自身が追加する属性とメソッドが含まれます。person_typ
オブジェクト型がidno
、name
およびphone
という3つの属性と、メソッドget_idno()
を持つ場合、person_typ
から導出されたオブジェクト型は、これらと同じ3つの属性とメソッドget_idno()
を持つことになります。person_typ
の定義を変更すると、サブタイプの定義も変更されます。
サブタイプは、次のようにキーワードUNDER
を使用して作成します。
CREATE
TYPE
student_typ
UNDER
person_typ
サブタイプの属性またはメソッドは、次の方法で特化します。
-
親スーパータイプが持たない新しい属性を追加します。
たとえば、
major
の属性を追加して、person_typ
の特殊な種類としてstudent_typ
を特化します。サブタイプは、親から継承した属性の型の削除や変更はできず、新しい属性を追加するのみです。 -
親スーパータイプにない新しいメソッドを追加します。
-
サブタイプのバージョンで親のバージョンとは異なるコードが実行されるように、サブタイプが継承するメソッドの一部の実装を変更します。
たとえば、ellipseオブジェクトがメソッド
calculate()
を定義するとします。ellipse_typ
の2つのサブタイプであるcircle_typ
およびsphere_typ
が、それぞれ別の方法でこのメソッドを実装することができます。
スーパータイプとそのサブタイプの間の継承関係が、オブジェクトの能力の大半をもたらす原因になっていると同時に、オブジェクトを複雑にする原因にもなっています。
スーパータイプのメソッドが変更でき、再コンパイルするのみですべての下位サブタイプにこの変更を反映させることができるというのは、非常に強力な機能です。しかし、それは同時に、型を特化するか、それともメソッドを再定義するかを考慮する必要があるということでもあります。また、階層内の任意の型を表や列に入れることができるというのは強力な機能ですが、特定の場合にこれを行うかどうかを決定する必要があります。さらに、必要な範囲の型のみが型階層から選択されるように、DML文や問合せに制約を付ける必要があることもあります。
関連項目:
-
完全な例は、例2-15を参照してください
2.3.3 継承のためのFINALとNOT FINALの型およびメソッド
オブジェクト型は継承可能にすることができ、メソッドはオーバーライド可能であると定義されている場合はオーバーライドできます。
継承可能にするオブジェクト型またはメソッドの場合、定義でそれが継承可能であることを指定する必要があります。型とメソッドのどちらにも、継承可能かどうかを決定するためにキーワードFINAL
またはNOT
FINAL
を使用します。
-
オブジェクト型: オブジェクト型を継承可能にして、そのオブジェクト型からサブタイプを導出できるようにするには、オブジェクト定義でそのように指定する必要があります。
NOT
FINAL
は、サブタイプを導出できることを意味します。FINAL
(デフォルト)は、この型からサブタイプを導出できないことを意味します。 -
メソッド: 定義でオーバーライドが可能かどうかを指定する必要があります。
NOT
FINAL
(デフォルト)は、メソッドをオーバーライドできることを意味します。FINAL
は、サブタイプは自身の実装を組み込むことでメソッドをオーバーライドすることはできないことを意味します。
関連項目:
2.3.3.1 FINALメンバー・ファンクションが含まれるNOT FINALとしてのオブジェクト型の作成
例2-12のように、FINAL
メンバー・ファンクションが含まれるNOT FINAL
オブジェクト型を作成できます。
例2-12 FINALメンバー・ファンクションが含まれるNOT FINALとしてのオブジェクト型の作成
DROP TYPE person_typ FORCE; -- above necessary if you have previously created object CREATE OR REPLACE TYPE person_typ AS OBJECT ( idno NUMBER, name VARCHAR2(30), phone VARCHAR2(20), FINAL MAP MEMBER FUNCTION get_idno RETURN NUMBER) NOT FINAL; /
2.3.3.2 NOT FINALオブジェクト型の作成
NOT FINAL
としてのオブジェクト型を作成できます。
例2-13では、person_typ
をNOT FINAL
型として宣言して、person_typ
のサブタイプを定義できるようにしています。
例2-13 NOT FINALとしてのperson_typオブジェクト型の作成
DROP TYPE person_typ FORCE; -- above necessary if you have previously created object CREATE OR REPLACE TYPE person_typ AS OBJECT ( idno NUMBER, name VARCHAR2(30), phone VARCHAR2(20)) NOT FINAL; /
2.3.4 NOT FINALへのFINAL型の変更
継承を変更するには、ALTER
TYPE
文を使用してFINAL型をNOT FINAL型に、またはその逆に変更します。
次に示す文は、person_typ
をFINAL型に変更します。
ALTER TYPE person_typ FINAL;
型をNOT
FINAL
からFINAL
に変更できるのは、ターゲット型がサブタイプを持たない場合のみです。
2.3.5 サブタイプの作成
サブタイプは、CREATE
TYPE
文を使用して作成します。この文は、UNDER
キーワードにより、サブタイプの直系の親を指定します。
内容は次のとおりです。
2.3.5.1 親オブジェクトまたはスーパータイプ・オブジェクトの作成
親オブジェクトまたはスーパータイプ・オブジェクトは、CREATE TYPE
文を使用して作成できます。
例2-14では、例2-15、例2-18および例2-19のサブタイプ定義を示すために、親またはスーパータイプのperson_typ
オブジェクトを作成します。
例2-14のshow()
に注意してください。続くサブタイプの各例では、OVERRIDING
キーワードにより、親タイプのshow()
ファンクションが各サブタイプの指定にオーバーライドされています。
例2-14 親またはスーパータイプのperson_typオブジェクトの作成
DROP TYPE person_typ FORCE; -- if created CREATE OR REPLACE TYPE person_typ AS OBJECT ( idno NUMBER, name VARCHAR2(30), phone VARCHAR2(20), MAP MEMBER FUNCTION get_idno RETURN NUMBER, MEMBER FUNCTION show RETURN VARCHAR2) NOT FINAL; / CREATE OR REPLACE TYPE BODY person_typ AS MAP MEMBER FUNCTION get_idno RETURN NUMBER IS BEGIN RETURN idno; END; -- function that can be overriden by subtypes MEMBER FUNCTION show RETURN VARCHAR2 IS BEGIN RETURN 'Id: ' || TO_CHAR(idno) || ', Name: ' || name; END; END; /
2.3.5.2 サブタイプのオブジェクトの作成
サブタイプは、スーパータイプの属性およびメソッドを継承します。
継承するものは次のとおりです。
-
スーパータイプに宣言または継承されているすべての属性
-
スーパータイプに宣言または継承されているすべてのメソッド
例2-15では、student_typ
オブジェクトをperson_typ
のサブタイプとして定義し、person_typ
で宣言または継承されているすべての属性およびperson_typ
で宣言または継承されているメソッドを継承します。
例2-15 UNDER句を使用したstudent_typサブタイプの作成
-- requires Ex. 2-14 CREATE TYPE student_typ UNDER person_typ ( dept_id NUMBER, major VARCHAR2(30), OVERRIDING MEMBER FUNCTION show RETURN VARCHAR2) NOT FINAL; / CREATE TYPE BODY student_typ AS OVERRIDING MEMBER FUNCTION show RETURN VARCHAR2 IS BEGIN RETURN (self AS person_typ).show || ' -- Major: ' || major ; END; END; /
student_typ
の定義文は、2つの新しい属性(dept_id
およびmajor
)を追加してperson_typ
を特化し、show
メソッドをオーバーライドしています。サブタイプの中で宣言する新しい属性には、自身の型階層内の上位に位置するそのスーパータイプのいずれかに宣言されている属性またはメソッドとは別の名前を持たせる必要があります。
2.3.5.3 一般的な起動
一般的な起動は、特定のサブタイプ・メンバー・メソッドではなく、スーパータイプまたは親タイプのメソッドを起動するメカニズムを提供します。
例2-15は、次の構文を使用してこのことを示しています。
(SELF AS person_typ).show
student_typ
show
メソッドは、person_typ
show
メソッドをコールして一般的な処理を実行した後で、そのperson_typ
show
メソッドによって戻された値に'--Major:'
を追加するという独自の処理を実行します。このように、サブタイプのメソッドのオーバーライドは、対応する親タイプのメソッドのオーバーライドをコールし、一般的な処理を実行してから独自の処理を実行できます。
メソッドは、標準のメンバー・メソッド同様に起動されます。ただし、AS
の後の型名は、式が評価する型の親タイプの型名である必要があります。
2.3.5.4 一般的な起動の使用
例2-16には、標準のメンバー・メソッド起動時の暗黙的自己引数に似た暗黙的SELF
引数が示されています。この場合は、限定的なstudent_typ
show
メソッドではなく、person_typ
show
メソッドが起動されます。
例2-16 一般的な起動の使用
-- Requires Ex. 2-14 and 2-15
DECLARE
myvar student_typ := student_typ(100, 'Sam', '6505556666', 100, 'Math');
name VARCHAR2(100);
BEGIN
name := (myvar AS person_typ).show; --Generalized invocation
END;
/
2.3.5.5 一般的な式の使用
メンバー・メソッドの起動と同様に、一般的な式もメソッドが明示的自己引数で起動される場合にサポートされます。
例2-17 一般的な式の使用
-- Requires Ex. 2-14 and 2-15
DECLARE
myvar2 student_typ := student_typ(101, 'Sam', '6505556666', 100, 'Math');
name2 VARCHAR2(100);
BEGIN
name2 := person_typ.show((myvar2 AS person_typ)); -- Generalized expression
END;
/
この例で二重カッコを使用しているのは、((myvar2
AS
person_typ))
は解決が必要な式であると同時にshow
ファンクションのパラメータでもあるためです。
注意: コンストラクタ・メソッドの起動にこの構文は使用できません。また、この構文でAS
の後に示される型名は、メソッドが起動される式の型の親タイプの名前である必要があります。
この構文を使用できるのは、対応する親タイプのメンバー・メソッドのオーバーライドを起動する場合のみです。
2.3.5.6 複数のサブタイプの作成
型には、複数の子サブタイプを持たせることができ、これらのサブタイプにもサブタイプを持たせることができます。
例2-18では、例2-15で作成した既存のサブタイプstudent_typ
に加えて、person_typ
の下に別のサブタイプemployee_typ
を作成します。
例2-18 UNDER句を使用したemployee_typサブタイプの作成
-- requires Ex. 2-14 DROP TYPE employee_typ FORCE; -- if previously created CREATE OR REPLACE TYPE employee_typ UNDER person_typ ( emp_id NUMBER, mgr VARCHAR2(30), OVERRIDING MEMBER FUNCTION show RETURN VARCHAR2); / CREATE OR REPLACE TYPE BODY employee_typ AS OVERRIDING MEMBER FUNCTION show RETURN VARCHAR2 IS BEGIN RETURN (SELF AS person_typ).show|| ' -- Employee Id: ' || TO_CHAR(emp_id) || ', Manager: ' || mgr ; END; END; /
2.3.5.7 別のサブタイプの下でのサブタイプの作成
別のサブタイプの下に、サブタイプを定義できます。
新しく定義されたサブタイプは、その親タイプが持つすべての属性およびメソッド(宣言されているもの、および継承されたもの)を継承します。例2-19では、例2-15で作成したstudent_typ
の下に新しいサブタイプpart_time_student_typ
を定義します。この新しいサブタイプは、student_typ
のすべての属性およびメソッドを継承し、別の属性number_hours
を追加します。
例2-19 UNDER句を使用したpart_time_student_typサブタイプの作成
CREATE TYPE part_time_student_typ UNDER student_typ (
number_hours NUMBER,
OVERRIDING MEMBER FUNCTION show RETURN VARCHAR2);
/
CREATE TYPE BODY part_time_student_typ AS
OVERRIDING MEMBER FUNCTION show RETURN VARCHAR2 IS
BEGIN
RETURN (SELF AS person_typ).show|| ' -- Major: ' || major ||
', Hours: ' || TO_CHAR(number_hours);
END;
END;
/
2.3.5.8 スーパータイプおよびサブタイプのオブジェクトを持つ表の作成
スーパータイプおよびサブタイプのインスタンスを含む表を作成できます。
例2-20のperson_obj_table
に示すように、作成した表にデータを移入できます。
例2-20 オブジェクト表の代入可能な行への値の挿入
CREATE TABLE person_obj_table OF person_typ; INSERT INTO person_obj_table VALUES (person_typ(12, 'Bob Jones', '650-555-0130')); INSERT INTO person_obj_table VALUES (student_typ(51, 'Joe Lane', '1-650-555-0140', 12, 'HISTORY')); INSERT INTO person_obj_table VALUES (employee_typ(55, 'Jane Smith', '1-650-555-0144', 100, 'Jennifer Nelson')); INSERT INTO person_obj_table VALUES (part_time_student_typ(52, 'Kim Patel', '1-650-555-0135', 14, 'PHYSICS', 20));
次のように、表のスーパータイプとサブタイプについてshow()
ファンクションをコールできます。
SELECT p.show() FROM person_obj_table p;
出力は次のようになります。
Id: 12, Name: Bob Jones
Id: 51, Name: Joe Lane -- Major: HISTORY
Id: 55, Name: Jane Smith -- Employee Id: 100, Manager: Jennifer Nelson
Id: 52, Name: Kim Patel -- Major: PHYSICS, Hours: 20
show()
メソッドが表示するデータは、オブジェクトがスーパータイプかサブタイプか、またサブタイプのshow()
メソッドがオーバーライドされているかどうかで異なることに注意してください。たとえば、Bob Jonesはperson_typ
、つまりスーパータイプです。name
とId
のみが表示されます。Joe Laneはstudent_typ
で、name
とId
はスーパータイプのshow()
ファンクションから提供され、major
はサブタイプのオーバーライドされたshow()
ファンクションから提供されます。
2.3.6 NOT INSTANTIABLEの型とメソッド
型およびメソッドは、作成時にNOT
INSTANTIABLE
として宣言できます。
NOT
INSTANTIABLE
の型とメソッドは、次のとおりです。
-
NOT
INSTANTIABLE
型型がNOT INSTANTIABLEの場合、その型のインスタンスはインスタンス化できません。その型に(デフォルトまたはユーザー定義を問わず)コンストラクタはありません。これを型に使用するとすれば、その型をスーパータイプとしてのみ使用し、そこから特化されたサブタイプをインスタンス化する場合です。
-
NOT
INSTANTIABLE
メソッドインスタンス化不可のメソッドは、プレースホルダの役目を果します。これは宣言されますが、型の中に実装されません。すべてのサブタイプにおいてそれぞれ独自の方法でメソッドをオーバーライドする場合に、インスタンス化不可のメソッドを定義します。この場合、スーパータイプにメソッドを定義しても有益ではありません。
インスタンス化可能な型からインスタンス化不可の型への切替え、またその逆の切替えには、ALTER
TYPE
文を使用します。
インスタンス化不可のメソッドを含む型は、例2-21のように、その型自体をインスタンス化不可として定義する必要があります。
2.3.7 インスタンス化不可のオブジェクト型の作成
インスタンス化不可の継承メソッドが、サブタイプにおいても実装されない場合、スーパータイプと同様にサブタイプそのものをインスタンス化不可として定義する必要があります。
インスタンス化不可のサブタイプは、インスタンス化不可のスーパータイプの下に定義できます。
例2-21 NOT INSTANTIABLEとしてのオブジェクト型の作成
DROP TYPE person_typ FORCE; -- if previously created CREATE OR REPLACE TYPE person_typ AS OBJECT ( idno NUMBER, name VARCHAR2(30), phone VARCHAR2(20), NOT INSTANTIABLE MEMBER FUNCTION get_idno RETURN NUMBER) NOT INSTANTIABLE NOT FINAL;/
2.3.8 INSTANTIABLEへのオブジェクト型の変更
ALTER
TYPE
文により、インスタンス化不可の型をインスタンス化可能にすることができます。
例2-22では、ALTER
TYPE
文によってperson_typ
がインスタンス化可能になります。
例2-22 INSTANTIABLEへのオブジェクト型の変更
CREATE OR REPLACE TYPE person_typ AS OBJECT ( idno NUMBER, name VARCHAR2(30), phone VARCHAR2(20)) NOT INSTANTIABLE NOT FINAL;/ ALTER TYPE person_typ INSTANTIABLE;
インスタンス化不可の型への変更
インスタンス化可能な型からインスタンス化不可の型に変更できる型は、その型を直接または別の型やサブタイプを介して間接的に参照する列、ビュー、表またはインスタンスを持たないものに限定されます。
インスタンス化不可の型をFINAL
として宣言することはできません。これは実際に意味がありません。
2.3.9 オーバーロードおよびオーバーライドされるメソッド
2.3.9.1 メソッドのオーバーロード
継承されたメソッドと同名の新しいメソッドをサブタイプに追加することを、オーバーロードといいます。
名前が同じでシグネチャが異なるメソッドが同じユーザー定義型に存在する場合、それらのメソッドはオーバーロードと呼ばれます。
メソッド・シグネチャは、メソッドの名前、数値、型、およびメソッドの仮パラメータ(暗黙的なself
パラメータなど)の順序で構成されます。
様々な動作方法を用意するには、オーバーロードが役立ちます。たとえば、ellipseオブジェクトは、異なる形態の計算を可能にするためにcalculate()
メソッドを別のcalculate()
メソッドでオーバーロードできます。
1つの型にオーバーロードされたメソッドが複数ある場合、コンパイラはメソッド・シグネチャを使用して、どのメソッドをコールするかを判断します。
次の疑似コードのサブタイプcircle_typ
は、calculate()
のオーバーロードを作成します。
CREATE TYPE ellipse_typ AS OBJECT (...,
MEMBER PROCEDURE calculate(x NUMBER, x NUMBER),
) NOT FINAL;
CREATE TYPE circle_typ UNDER ellipse_typ (...,
MEMBER PROCEDURE calculate(x NUMBER),
...);
circle_typ
には、2つのバージョンのcalculate()
が含まれます。1つは、NUMBER
パラメータを2つ持つ継承されたバージョンで、もう1つはNUMBER
パラメータを1つ持つ新しく作成されたメソッドです。
2.3.9.2 メソッドのオーバーライドおよび隠蔽
あるサブタイプの動作をカスタマイズするために、継承されたメソッドを再定義することを、メンバー・メソッドの場合はオーバーライド、静的メソッドの場合は隠蔽といいます。
オーバーロードとは異なり、新しいメソッドは作成せず、キーワードOVERRIDING
を使用して既存のメソッドを再定義するだけです。
オーバーライドおよび隠蔽は、サブタイプにおいて継承メソッドにこれまでとは異なる動作をさせるために、継承メソッドを再定義することです。たとえば、ellipse_typ
スーパータイプから導出されたサブタイプcircle_typ
は、円の面積を求めるようにメンバー・メソッドcalculate()
をカスタマイズするために、このメソッドをオーバーライドします。メソッドをオーバーライドする例は、「サブタイプの作成」を参照してください。
いずれの場合も、サブタイプで再定義されたメソッドのバージョンが、同じ名前およびシグネチャを持つ元のバージョンを隠し、サブタイプのインスタンスがメソッドを起動する際には、元のバージョンのかわりに新しいバージョンが実行されるという点では、オーバーライドと隠蔽は似ています。サブタイプ自身がサブタイプを持つ場合は、元のメソッドではなく、再定義されたメソッドを継承します。
オーバーライドの場合、実行するメソッドの正しいバージョンを動的に選択するために、システムは、メンバー・メソッドの暗黙的自己引数に含まれている型情報に依存します。隠蔽の場合、正しいバージョンはコンパイル時に識別されるため、動的ディスパッチは必要ありません。「動的メソッド・ディスパッチ」を参照してください。
メソッドをオーバーライドまたは隠蔽するには、メソッドのシグネチャを保持する必要があります。メソッドのオーバーロードは、すべて同じ名前を持つため、コンパイラはサブタイプのメソッドのシグネチャを使用して、置き換えられるスーパータイプのバージョンを識別します。
CREATE
TYPE
BODY
文の中でOVERRIDING
キーワードを使用してオーバーライドを通知します。サブタイプが静的メソッドを隠蔽する場合、これは必要ありません。
次の疑似コードでは、サブタイプがメソッドcalculate()
をオーバーライドしていることを通知しています。
CREATE TYPE ellipse_typ AS OBJECT (...,
MEMBER PROCEDURE calculate(),
FINAL MEMBER FUNCTION function_mytype(x NUMBER)...
) NOT FINAL;
CREATE TYPE circle_typ UNDER ellipse_typ (...,
OVERRIDING MEMBER PROCEDURE calculate(),
...);
この階層のダイアグラムは、図2-2を参照してください。
2.3.9.3 メソッドのオーバーライドの制限事項
メソッドのオーバーライドには、一定の制限事項があります。
-
オーバーライドできるメソッドは、スーパータイプでFINALとして宣言されていないもののみです。
-
オーダー・メソッドが出現する可能性があるのは、型階層のルート型のみで、サブタイプで再定義(オーバーライド)することはできません。
-
サブタイプの静的メソッドにより、スーパータイプのメンバー・メソッドを再定義することはできません。
-
サブタイプのメンバー・メソッドにより、スーパータイプの静的メソッドを再定義することはできません。
-
オーバーライドされているメソッドが、いずれかのパラメータのデフォルト値を持つ場合は、そのパラメータのデフォルト値と同じものを、オーバーライドするメソッドに持たせる必要があります。
2.3.10 動的メソッド・ディスパッチ
動的メソッド・ディスパッチとは、現在の型または指定された型から上方向に型階層を調べて、実行時にメソッド・コールを最も近い実装にディスパッチする方法のことです。
動的メソッド・ディスパッチは、メンバー・メソッドをオーバーライドするときにのみ使用可能であり、静的メソッドには適用されません。
メソッドのオーバーライドにより、型階層で同じメソッドの複数の実装を定義できます。次に示すellipse_typ
型、circle_typ
型、sphere_typ
型の階層の中では、それぞれの型が異なる方法でcalculate
メソッドを定義できます。
これらのメソッドの1つが起動されると、メソッドを起動したオブジェクト・インスタンスの型により、そのメソッドが使用する実装が決まります。次に、この実装にコールがディスパッチされ、実行されます。メソッド実装を選択するこのプロセスは、コンパイル時ではなく、実行時に処理されるため、仮想メソッド・ディスパッチまたは動的メソッド・ディスパッチと呼ばれます。
メソッド・コールは型階層を上方向に調べます。下方向には調べません。コールによりオブジェクト・インスタンスのメンバー・メソッドが起動される場合は、そのインスタンスの型が現在の型となり、その型で定義または継承されている実装が使用されます。コールにより型の静的メソッドが起動される場合は、その指定の型で定義または継承されている実装が使用されます。
関連項目:
サブプログラム・コールの解決方法の詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください
2.3.11 型階層内の型の代入
型階層内で型を扱うときは、最も一般的なレベル(全員の選択や更新など)で作業する必要がある場合があります。しかし、学生のみ、または学生以外の個人のみなど、特定のサブタイプのみを選択または更新する必要がある場合もあります。
全員を選択し、宣言された型がperson_typ
のオブジェクトのみでなく、宣言されたサブタイプがstudent_typ
またはemployee_typ
のオブジェクトも返す(多様な性質を持つ)機能は、代入性と呼ばれます。宣言された型がスーパータイプである変数または列に、スーパータイプの下位のサブタイプの1つが使用できる場合、スーパータイプは代入可能です。
一般的に、型は代入可能です。オブジェクト属性、コレクション要素およびREF
は代入可能です。REF
、型またはperson_typ
型のコレクションとして定義された属性には、person_typ
のインスタンス、そのインスタンスのインスタンス、またはperson_typ
のサブタイプのインスタンスのREF
を入れることができます。
サブタイプがスーパータイプの単なる特殊な種類である場合は、型が代入可能であることを期待します。ただし、形式上、サブタイプは元々型の1つで、上位のスーパータイプと同じ型ではありません。学生全員や、就業者全員など、個人全員が入っている列には、複数の型のデータが実際に格納されています。
基本的に、オブジェクト属性、コレクション要素およびREF
は常に代入可能です。型定義のレベルでは、これらの代入性をあるサブタイプに制限する構文はありません。ただし、特定の表や列について、記憶域レベルの代入性を無効にすることも、制約を付けることもできます。
関連項目:
-
「代入性の制約」
2.3.12 列および行の代入性
オブジェクト表およびビューの中のオブジェクト型の列と行は代入可能です。つまり、特定の型の列または行にはその型とそのサブタイプのインスタンスを入れることができます。
内容は次のとおりです。
2.3.12.1 列および行の代入性について
オブジェクト表の中のオブジェクト型の列と行は代入できます。
例2-14に示したような型階層person_typ
について考えます。すべての型の行が含まれるperson_typ
のオブジェクト表を作成できます。このためには、例2-20で示したように、特定の型のインスタンスを、INSERT
文のVALUES
句の中でその型のコンストラクタを使用してオブジェクト表に挿入します。
同様に、例2-23に示すように、リレーショナル表またはビューで型person_typ
の代入可能列に3つの型すべてのインスタンスを入れることもできます。この例では、その型階層から個人、学生および定時制学生のオブジェクトを再作成して、person_typ
列contact
に挿入します。
例2-23 表の代入可能な列への値の挿入
DROP TYPE person_typ FORCE; -- if previously created DROP TYPE student_typ FORCE; -- if previously created DROP TYPE part_time_student_typ FORCE; -- if previously created DROP TABLE contacts; if previously created CREATE OR REPLACE TYPE person_typ AS OBJECT ( idno NUMBER, name VARCHAR2(30), phone VARCHAR2(20)) NOT FINAL;/ CREATE TYPE student_typ UNDER person_typ ( dept_id NUMBER, major VARCHAR2(30)) NOT FINAL; / CREATE TYPE part_time_student_typ UNDER student_typ ( number_hours NUMBER); / CREATE TABLE contacts ( contact person_typ, contact_date DATE ); INSERT INTO contacts VALUES (person_typ (12, 'Bob Jones', '650-555-0130'), '24 Jun 2003' ); INSERT INTO contacts VALUES (student_typ(51, 'Joe Lane', '1-650-555-0178', 12, 'HISTORY'), '24 Jun 2003' ); INSERT INTO contacts VALUES (part_time_student_typ(52, 'Kim Patel', '1-650-555-0190', 14, 'PHYSICS', 20), '24 Jun 2003' );
新しく作成したサブタイプは、代入可能な表およびそのスーパータイプの列のいずれにも(サブタイプを作成する前に存在していた表や列など)格納できます。
一般に、属性はドット表記法を使用してアクセスできます。1つの行または列の宣言された型のサブタイプの属性にアクセスするには、TREAT
ファンクションを使用します。次に例を示します。
SELECT TREAT(contact AS student_typ).major FROM contacts;
「TREAT」を参照してください。
2.3.12.2 代入可能な行でのOBJECT_VALUEおよびOBJECT_IDの使用
代入可能な行のオブジェクト識別子(OID)および値にアクセスし、識別できます。
例2-24に示すように、OBJECT_VALUE
およびOBJECT_ID
疑似列を使用して、オブジェクト表内の代入可能な行の値およびオブジェクト識別子にアクセスし、識別できるようにします。
例2-24 OBJECT_VALUEおよびOBJECT_IDの使用
DROP TABLE person_obj_table; -- required if previously created CREATE TABLE person_obj_table OF person_typ; INSERT INTO person_obj_table VALUES (person_typ(20, 'Bob Jones', '650-555-0130')); SELECT p.object_id, p.object_value FROM person_obj_table p;
2.3.12.3 スーパータイプの属性を持つサブタイプ
サブタイプは、型がスーパータイプの型である属性を持つことが可能です。次に例を示します。
例2-25 スーパータイプの属性を持つサブタイプの作成
-- requires Ex 2-22 CREATE TYPE student_typ UNDER person_typ ( dept_id NUMBER, major VARCHAR2(30), advisor person_typ); /
ただし、このタイプの列は代入可能ではありません。同様に、サブタイプは、要素の型がそのスーパータイプの1つになっているコレクション属性を持つことができますが、この場合もこのタイプの列は代入可能ではありません。たとえば、student_typ
にperson_typ
のネストした表またはVARRAYがある場合、student_typ
列は代入可能ではありません。
ただし、スーパータイプを参照するREF
属性を持つサブタイプの代入可能な列が定義できます。たとえば、例2-26に示すcomposite_category_typ
サブタイプには、ネストした表subcategory_ref_list
が含まれています。この表には、category_typ
のREFであるsubcategory_ref_list_typ
が含まれています。サブタイプは、次のようにして作成されました。
例2-26 REF属性を持つサブタイプの列の定義
-- not to be executed CREATE TYPE subcategory_ref_list_typ AS TABLE OF REF category_typ; / CREATE TYPE composite_category_typ UNDER category_typ ( subcategory_ref_list subcategory_ref_list_typ ...
「新しい表での代入性の無効化」を参照してください。
2.3.12.4 REF列および属性の代入
REF
列および属性は、ビューでも表でも代入可能です。たとえば、ビューまたは表のいずれでも、REF
person_typ
として宣言された列に、person_typ
またはそのサブタイプのインスタンスへの参照を入れることができます。
2.3.13 代入可能な列に格納される新規作成したサブタイプ
サブタイプを作成すると、スーパータイプの代入可能な列をすでに持つ表に、新しいサブタイプも格納できるようになります。
つまり、サブタイプの作成には、このような表の有無が影響します。このような表が存在する場合、代入可能なサブタイプ(表の制約に違反しないサブタイプ)のみ作成できます。
次の例では、person_typ
を作成してから、person_typ
の下にstudent_typ
サブタイプを作成しようとします。
例2-27 代入可能な列の作成後のサブタイプの作成
DROP TYPE person_typ FORCE;
DROP TABLE person_obj_table;
DROP TYPE student_typ;
-- perform above drops if objects/tables created
CREATE OR REPLACE TYPE person_typ AS OBJECT (
idno NUMBER,
name VARCHAR2(30),
phone VARCHAR2(20))
NOT FINAL;/
CREATE TABLE person_obj_table (p person_typ);
ただし、student_typ
はスーパータイプの属性を持ち、person_obj_table
表に、スーパータイプの代入可能な列p
があるため、次の文は正常に実行されません。
CREATE TYPE student_typ UNDER person_typ ( -- incorrect CREATE subtype
advisor person_typ);
/
次の操作は正常に実行されます。このようなstudent_typ
サブタイプは、代入可能です。person_obj_table
表は、自動的にこの新しい型のインスタンスを格納できるようになります。
CREATE TYPE student_typ UNDER person_typ ( dept_id NUMBER, major VARCHAR2(30));/ INSERT INTO person_obj_table VALUES (student_typ(51, 'Joe Lane', '1-650-555-0178', 12, 'HISTORY'));
2.3.14 代入可能な列の作成後のサブタイプの削除
VALIDATE
オプションを使用してサブタイプを削除すると、スーパータイプの代入可能な列のいずれにもサブタイプのインスタンスが格納されていないことが確認されます。このようなインスタンスが存在しない場合は、DROP
操作が完了します。
次の文は、person_obj_table
表の代入可能な列p
にstudent_typ
のインスタンスが格納されているため、正常に実行されません。
DROP TYPE student_typ VALIDATE -- incorrect: an instance still exists ;
型を削除するには、最初にスーパータイプの代入可能な列にあるそのインスタンスを削除します。
-- Delete from table and drop student_typ subtype example, not sample schema
DELETE FROM person_obj_table WHERE p IS OF (student_typ); DROP TYPE student_typ VALIDATE;
関連項目:
DROP
およびVALIDATE
の詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。
2.3.15 新しい表での代入性の無効化
表を作成する際、埋込み属性やネストされたコレクションを含め、列または属性についての代入性はすべて無効にできます。
表を作成する際、NOT
SUBSTITUTABLE
AT
ALL
LEVELS
句を使用します。
これにより、埋込み属性や任意のレベルにネストされたコレクションを含め、列または属性の代入性はすべて無効になります。
次の例に示す句は、office_typ
インスタンスのみを格納するように、リレーショナル表のoffice
列に制約を設定し、サブタイプのどのインスタンスも使用できなくします。
例2-28 表の作成時の代入性の無効化
DROP TYPE location_typ FORCE; -- required if previously created
DROP TYPE office_typ FORCE; -- required if previously created
CREATE OR REPLACE TYPE location_typ AS OBJECT (
building_no NUMBER,
city VARCHAR2(40) );
/
CREATE TYPE people_typ AS TABLE OF person_typ;
/
CREATE TYPE office_typ AS OBJECT (
office_id VARCHAR(10),
location location_typ,
occupant person_typ )
NOT FINAL;/
CREATE TABLE dept_office (
dept_no NUMBER,
office office_typ)
COLUMN office NOT SUBSTITUTABLE AT ALL LEVELS;
オブジェクト表の場合は、次のように句を表全体に適用できます。
DROP TABLE office_tab; -- if previously created CREATE TABLE office_tab OF office_typ NOT SUBSTITUTABLE AT ALL LEVELS;
また、この句で特定の列(つまり、表のオブジェクト型の特定の属性)の代入性を無効にすることもできます。
DROP TABLE office_tab; -- if previously created CREATE TABLE office_tab OF office_typ COLUMN occupant NOT SUBSTITUTABLE AT ALL LEVELS;
次のような構文を使用すると、コレクションの要素型が代入性を持たないように指定できます。
DROP TABLE people_tab;
-- required if previously created
CREATE TABLE people_tab (
people_column people_typ )
NESTED TABLE people_column
NOT SUBSTITUTABLE AT ALL LEVELS STORE AS people_column_nt;
REF
列の代入性を無効にするメカニズムはありません。
オブジェクト列に制約を付けるには、NOT
SUBSTITUTABLE
AT
ALL
LEVELS
かIS
OF
typeのいずれか一方のみが使用可能で、両方は使用できません。
2.3.16 代入性の制約
オブジェクト列または属性で許可されているサブタイプの範囲を、宣言された型の階層内の特定のサブタイプに制限するように、制約を加えることができます。
これを行うには、IS
OF
type
の制約を使用します。
次の文は、占有者が従業員のみに限定されるoffice_typ
の表を作成します。
例2-29 表の作成時の代入性の制約
DROP TABLE office_tab; -- if previously created CREATE TABLE office_tab OF office_typ COLUMN occupant IS OF (ONLY employee_typ);
office_typ
型がperson_typ
型の占有者を許可している場合でも、列宣言によりemployee_typ
のインスタンスのみを格納するように制約されます。
行オブジェクトおよび列オブジェクトを(複数ではなく)1つのサブタイプに限定する場合、使用できるのはIS
OF
type
の演算子のみで、前述の例のようにONLY
キーワードを使用する必要があります。
オブジェクト列に制約を付けるには、IS
OF
type
かNOT
SUBSTITUTABLE
AT
ALL
LEVELS
のいずれか一方のみが使用可能で、両方は使用できません。
2.3.17 表での代入性の変更
既存の表で、ALTER
TABLE
文を使用して、オブジェクト列をSUBSTITUTABLE
からNOT
SUBSTITUTABLE
(またはNOT
SUBSTITUTABLE
からSUBSTITUTABLE
)に変更できます。
特定の列に対し、[NOT
] SUBSTITUTABLE
AT
ALL
LEVELS
句をALTER
TABLE
文に指定します。
代入性は、特定の列に対してのみ変更できます。オブジェクト表全体の代入性は変更できません。
次の文により、列office
が代入可能になります。
例2-30 表内の代入性の変更
-- Requires Ex. 2-28
ALTER TABLE dept_office
MODIFY COLUMN office SUBSTITUTABLE AT ALL LEVELS;
次の文により、列が代入不可になります。FORCE
キーワードも使用されている点に注意してください。このキーワードは、サブタイプ属性の型ID情報またはデータが含まれている非表示列を削除します。
-- Alter table substitutability with FORCE
ALTER TABLE dept_office
MODIFY COLUMN office NOT SUBSTITUTABLE AT ALL LEVELS FORCE;
--DROP TABLE dept_office;
列を代入不可にする際にFORCE
キーワードを使用しない場合、列および型の全属性をFINAL
にしないと、ALTER
TABLE
文の実行は失敗します。
VARRAY
列は、VARRAY自体の要素型がFINALであり、FINALでない埋込み型を(自らの属性内や埋込み型の属性内などに)所有しない場合のみ、SUBSTITUTABLE
からNOT
SUBSTITUTABLE
に変更できます。
関連項目:
型IDおよびサブタイプ属性の非表示列の詳細は、「代入可能な列およびオブジェクト表の非表示列」を参照してください。
2.3.18 代入性の変更の制限事項
ALTER
TABLE
文では、一度に1列の代入性のみ変更できます。
複数の列の代入性を変更する場合、複数の文を発行する必要があります。
オブジェクト表では、表の作成時に代入性が表レベルで明示的に設定されていない場合のみ、列の代入性を変更できます。
たとえば、次の例では、CREATE
TABLE
文により、代入性が明示的に表レベルで有効または無効に設定されていないため、列アドレスの代入性の変更は成功します。
DROP TABLE office_tab; -- if previously created CREATE TABLE office_tab OF office_typ; ALTER TABLE office_tab MODIFY COLUMN occupant NOT SUBSTITUTABLE AT ALL LEVELS FORCE;
ただし、次の例では、代入性が表レベルで明示的に設定されているため、列アドレスの設定の変更は失敗します。
DROP TABLE office_tab; -- if previously created CREATE TABLE office_tab OF office_typ NOT SUBSTITUTABLE AT ALL LEVELS; /* Following SQL statement generates an error: */ ALTER TABLE office_tab MODIFY COLUMN occupant SUBSTITUTABLE AT ALL LEVELS FORCE -- incorrect ALTER;
IS
OF
type
演算子によってすでに代入性が制約されている列は、[NOT
] SUBSTITUTABLE
AT
ALL
LEVELS
句によって代入性を変更することはできません。
関連項目:
IS
OF
type
の詳細は、「代入性の制約」を参照してください。
2.3.19 型をまたがる代入
この項で説明する代入規則は、INSERT/UPDATE
文、RETURNING
句、ファンクションのパラメータ、およびPL/SQL変数に適用されます。
内容は次のとおりです。
2.3.19.1 オブジェクト代入の代表的なオブジェクト
代入性とは、上位のスーパータイプの1つの代理を務めるサブタイプの能力のことです。
サブタイプのかわりにスーパータイプを使用するなど、逆の方向で置換を行うと、コンパイル時にエラーが発生します。
型source_typ
のソースを型target_typ
のターゲットに代入するパターンは、次のいずれかである必要があります。
-
ケース1:
source_typ
およびtarget_typ
が同一型である。 -
ケース2:
source_typ
がtarget_typ
のサブタイプである(ワイドニング)。
ケース2は、ワイドニングを示しています。
2.3.19.2 ワイドニング代入
ワイドニングとは、ターゲットが宣言された型よりも、ソースが宣言された型の方が範囲が特定される代入のことです。
ワイドニングの例として、個人タイプの変数への従業員インスタンスの代入があります。
個人の範囲を絞って定義し、対象を特化したのが従業員であるため、個人をさらに特化して従業員にする存在を無視する場合、個人用のスロットに従業員を入れることができます。すべての従業員が個人のため、ワイドニング代入は常に機能します。
次の表を使用して、ワイドニングについて説明します。
TABLE T(pers_col person_typ, emp_col employee_typ,
stu_col student_typ)
次に示す代入は、ワイドニングを示しています。perscol
が代入不可と定義されていないかぎり、代入は有効です。
UPDATE T set pers_col = emp_col;
次の例のPL/SQLでは、最初にperson_typ
およびemployee_typ
を作成する必要があります。
例2-31 PL/SQLの代入
DROP TYPE person_typ FORCE; -- if previously created CREATE TYPE person_typ AS OBJECT ( idno NUMBER, name VARCHAR2(30), phone VARCHAR2(20)) NOT FINAL; / DROP TYPE employee_typ FORCE; -- if previously created CREATE TYPE employee_typ UNDER person_typ ( emp_id NUMBER, mgr VARCHAR2(30)); / -- PL/SQL assignment example DECLARE var1 person_typ; var2 employee_typ; BEGIN var2 := employee_typ(55, 'Jane Smith', '1-650-555-0144', 100, 'Jennifer Nelson'); var1 := var2; END; /
2.3.19.3 ナローイング代入
ナローイング代入はワイドニングの逆です。
ナローイング代入では、個人など一般化した広い範囲を対象とする型を、従業員など範囲を限定して定義された型とみなすことを意味します。すべての個人が従業員であるとはかぎらないため、このような特定の代入は、該当する個人が偶然に従業員の場合のみ機能します。このため最終的には、「オブジェクト代入の代表的なオブジェクト」に示したように、ナローイング代入はケース1のような場合にしか機能しません。
ナローイング代入を行うには、TREAT
ファンクションを使用して、より一般的な宣言型のソース・インスタンスが実際には限定されたターゲット型のインスタンスであり、そのように操作可能であることをテストする必要があります。TREAT
ファンクションは実行時にこのことを確認するためのチェックを行い、ソース値(該当する個人)がターゲット型でもサブタイプの1つでもない場合は、NULL
を戻します。
たとえば、次のUPDATE
文は、列perscol
のperson_typ
の値を、employee_typ
の列empcol
に設定します。perscol
のそれぞれの値についての代入が成功するのは、この個人が従業員でもある場合です。個人が従業員ではない場合、TREAT
はNULL
を戻し、代入がNULL
に戻ります。
UPDATE T set emp_col = TREAT(pers_col AS employee_typ);
次の文は、ソース値の宣言された型を明示的に変更せずに、代入のナローイングを試行します。この文は、エラーを戻す結果になります。
UPDATE T set emp_col = pers_col;
関連項目:
2.3.19.4 コレクションの代入
コレクション型の式の代入では、ソースの宣言された型がターゲットのそれと一致する必要があります。
ワイドニングとナローイングのどちらも、コレクション型の式の代入では使用できません。しかし、スーパータイプのコレクションにサブタイプ値を代入できます。たとえば、新しいstudent_typ
を作成した後、次のようなコレクション型があるとします。
例2-32 コレクションperson_setの作成
-- Requires 2-21 DROP student_typ; -- if previously created CREATE TYPE student_typ UNDER person_typ ( dept_id NUMBER, major VARCHAR2(30)) NOT FINAL; / CREATE TYPE person_set AS TABLE OF person_typ; / CREATE TYPE student_set AS TABLE OF student_typ; /
これらの異なるコレクション型の式を相互に代入することはできませんが、student_typ
のコレクション要素はperson_set
型のコレクションに代入できます。
DECLARE var1 person_set; var2 student_set; elem1 person_typ; elem2 student_typ; BEGIN -- var1 := var2; /* ILLEGAL - collections not of same type */ var1 := person_set (elem1, elem2); /* LEGAL : Element is of subtype */ END; /