OBJECT TYPE は、Oracle Databaseで属性とメソッドをまとめて定義できるSQLオブジェクト型です。PL/SQLの中で「データと振る舞い」を一体にしたい場合、ネスト表やパイプライン関数の戻り値を型として整えたい場合に役立ちます。
ただし、実務では通常の表・レコード・パッケージで十分な場面も多く、安易にオブジェクト型へ寄せるとSQL、ORM、移行、依存関係が複雑になります。この記事では、OBJECT TYPEを使うべき場面と避けるべき場面を分けつつ、メンバーメソッド、コンストラクタ、MAP / ORDER、継承、ネスト表まで整理します。網羅的な仕様は Oracle オブジェクト型(CREATE TYPE)完全ガイド も参考になります。
CREATE TYPE ... AS OBJECTとTYPE BODYMEMBER FUNCTION/MEMBER PROCEDUREとSELF- コンストラクタ、ネスト表、オブジェクト列の使い方
MAP/ORDERメソッドによる比較・並び替えFINAL/NOT FINAL、継承、オーバーライドUSER_TYPES、USER_TYPE_ATTRS、USER_TYPE_METHODSによる確認
OBJECT TYPEとは
オブジェクト型は、属性とメソッドを持つユーザー定義型です。属性はデータ構造、メソッドはそのデータに対する処理を表します。Oracle公式ドキュメントでも、メンバーメソッドはオブジェクトインスタンスのデータへアクセスするための処理として説明されています。
基本構文
まず、顧客を表すオブジェクト型を定義します。型仕様に属性とメソッド宣言を書き、型本体にメソッド実装を書きます。
CREATE OR REPLACE TYPE customer_obj AS OBJECT (
cust_id NUMBER,
cust_name VARCHAR2(100),
email VARCHAR2(200),
MEMBER PROCEDURE show_info,
MEMBER FUNCTION domain RETURN VARCHAR2
);
/
CREATE OR REPLACE TYPE BODY customer_obj AS
MEMBER PROCEDURE show_info IS
BEGIN
DBMS_OUTPUT.PUT_LINE(
'ID=' || SELF.cust_id ||
', NAME=' || SELF.cust_name ||
', EMAIL=' || SELF.email
);
END show_info;
MEMBER FUNCTION domain RETURN VARCHAR2 IS
BEGIN
IF SELF.email IS NULL OR INSTR(SELF.email, '@') = 0 THEN
RETURN NULL;
END IF;
RETURN SUBSTR(SELF.email, INSTR(SELF.email, '@') + 1);
END domain;
END;
/
SELF は現在のオブジェクトインスタンスを表します。属性名だけでも参照できますが、実務では SELF.email のように書くと、ローカル変数や引数との区別が明確になります。
インスタンスを作成してメソッドを呼ぶ
オブジェクト型には暗黙のコンストラクタが作られます。属性順に値を渡してインスタンスを作成し、ドット記法でメソッドを呼び出します。メソッド呼び出しでは、引数がない関数でも括弧を付ける書き方にしておくと安全です。
DECLARE
l_customer customer_obj;
BEGIN
l_customer := customer_obj(
101,
'Sato Taro',
'taro@example.com'
);
l_customer.show_info();
DBMS_OUTPUT.PUT_LINE('domain=' || l_customer.domain());
END;
/
初期化していないオブジェクト変数の属性やメソッドに触ると、ORA-06530 になることがあります。未初期化の複合型エラーは ORA-06530の原因と解決方法 が参考になります。
独自コンストラクタを定義する
既定のコンストラクタは属性順にすべての値を渡します。初期値を補完したい、入力を検証したい、属性順に依存したくない場合は、独自コンストラクタを用意できます。
CREATE OR REPLACE TYPE customer_obj AS OBJECT (
cust_id NUMBER,
cust_name VARCHAR2(100),
email VARCHAR2(200),
CONSTRUCTOR FUNCTION customer_obj(
p_cust_id IN NUMBER,
p_cust_name IN VARCHAR2
) RETURN SELF AS RESULT,
MEMBER FUNCTION domain RETURN VARCHAR2
);
/
CREATE OR REPLACE TYPE BODY customer_obj AS
CONSTRUCTOR FUNCTION customer_obj(
p_cust_id IN NUMBER,
p_cust_name IN VARCHAR2
) RETURN SELF AS RESULT
IS
BEGIN
SELF.cust_id := p_cust_id;
SELF.cust_name := p_cust_name;
SELF.email := NULL;
RETURN;
END;
MEMBER FUNCTION domain RETURN VARCHAR2 IS
BEGIN
IF SELF.email IS NULL OR INSTR(SELF.email, '@') = 0 THEN
RETURN NULL;
END IF;
RETURN SUBSTR(SELF.email, INSTR(SELF.email, '@') + 1);
END;
END;
/
独自コンストラクタを増やすと便利ですが、属性追加や仕様変更時の影響も増えます。通常のパッケージAPIで生成を隠す設計も候補です。パッケージ設計の考え方は パッケージ設計でコード管理と再利用性を極める と合わせて考えると整理しやすくなります。
ネスト表で複数オブジェクトを扱う
オブジェクト型は、ネスト表やVARRAYの要素型として使えます。複数の顧客、明細、検証結果などをまとめて返したい場合に便利です。
CREATE OR REPLACE TYPE customer_tab AS TABLE OF customer_obj;
/
DECLARE
l_customers customer_tab := customer_tab();
BEGIN
l_customers.EXTEND(2);
l_customers(1) := customer_obj(1, 'Tanaka Hanako', 'hanako@example.com');
l_customers(2) := customer_obj(2, 'Yamada Jiro', 'jiro@example.com');
FOR i IN 1 .. l_customers.COUNT LOOP
l_customers(i).show_info();
END LOOP;
END;
/
コレクション型の基本は PL/SQL コレクション型完全ガイド、実戦パターンは コレクションを実戦活用する完全ガイド も参考になります。
SQLで使う場合
オブジェクト型はSQL型なので、テーブル列やSQLのSELECTでも使えます。ただし、オブジェクト列は便利な反面、検索・索引・ORM・移行で扱いが難しくなることがあります。
CREATE TABLE order_master (
order_id NUMBER PRIMARY KEY,
customer customer_obj
);
INSERT INTO order_master(order_id, customer)
VALUES (
1,
customer_obj(100, 'Takahashi Ichiro', 'ichiro@example.com')
);
SELECT o.order_id,
o.customer.cust_id,
o.customer.cust_name,
o.customer.domain() AS email_domain
FROM order_master o;
業務テーブルの主要データをオブジェクト列に入れると、後続の集計や検索で通常の列より扱いづらくなることがあります。長期運用の基幹テーブルでは、通常のリレーショナル列に分解し、オブジェクト型はAPI境界や戻り値に使う方が無難なことが多いです。
MAPメソッドで並び替える
オブジェクト同士を比較・並び替えしたい場合、MAP MEMBER FUNCTION または ORDER MEMBER FUNCTION を定義します。MAP はオブジェクトを比較可能なスカラー値へ写像する方法です。Oracle公式でも、MAPメソッドは比較やソートの基準になるスカラー値を返すと説明されています。
CREATE OR REPLACE TYPE amount_obj AS OBJECT (
currency_code VARCHAR2(3),
amount NUMBER,
MAP MEMBER FUNCTION sort_key RETURN NUMBER
);
/
CREATE OR REPLACE TYPE BODY amount_obj AS
MAP MEMBER FUNCTION sort_key RETURN NUMBER IS
BEGIN
RETURN NVL(SELF.amount, 0);
END;
END;
/
CREATE OR REPLACE TYPE amount_tab AS TABLE OF amount_obj;
/
SELECT VALUE(t).currency_code,
VALUE(t).amount
FROM TABLE(amount_tab(
amount_obj('JPY', 1200),
amount_obj('USD', 10),
amount_obj('EUR', 8)
)) t
ORDER BY VALUE(t);
ひとつのオブジェクト型に定義できる比較方法は、基本的に MAP か ORDER のどちらかです。多くのケースでは、比較基準が単一値で表せる MAP の方がシンプルです。
継承とオーバーライド
オブジェクト型は NOT FINAL にするとサブタイプを作れます。親型のメソッドを子型でオーバーライドする場合は、親側のメソッドもオーバーライド可能な設計にします。
CREATE OR REPLACE TYPE base_customer AS OBJECT (
cust_id NUMBER,
cust_name VARCHAR2(100),
MEMBER FUNCTION get_info RETURN VARCHAR2
) NOT FINAL;
/
CREATE OR REPLACE TYPE BODY base_customer AS
MEMBER FUNCTION get_info RETURN VARCHAR2 IS
BEGIN
RETURN 'customer=' || SELF.cust_name;
END;
END;
/
CREATE OR REPLACE TYPE corporate_customer UNDER base_customer (
company_name VARCHAR2(200),
OVERRIDING MEMBER FUNCTION get_info RETURN VARCHAR2
);
/
CREATE OR REPLACE TYPE BODY corporate_customer AS
OVERRIDING MEMBER FUNCTION get_info RETURN VARCHAR2 IS
BEGIN
RETURN 'company=' || SELF.company_name || ', contact=' || SELF.cust_name;
END;
END;
/
継承は強力ですが、型階層が深くなると依存関係と変更影響が読みにくくなります。通常のPL/SQL設計では、パッケージとレコード型で十分な場面も多いため、継承は本当に必要な場合に絞るのが現実的です。
JSON連携は自作メソッドとして扱う
注意点として、TO_JSON や FROM_JSON はOracleのすべてのオブジェクト型に自動で生える標準メソッドではありません。JSONへ変換したい場合は、JSON_OBJECT などのSQL/JSON関数を使うか、自作のメンバーメソッドとして実装します。
CREATE OR REPLACE TYPE customer_json_obj AS OBJECT (
cust_id NUMBER,
cust_name VARCHAR2(100),
email VARCHAR2(200),
MEMBER FUNCTION to_json RETURN CLOB
);
/
CREATE OR REPLACE TYPE BODY customer_json_obj AS
MEMBER FUNCTION to_json RETURN CLOB IS
l_json CLOB;
BEGIN
SELECT JSON_OBJECT(
'custId' VALUE SELF.cust_id,
'name' VALUE SELF.cust_name,
'email' VALUE SELF.email
RETURNING CLOB
)
INTO l_json
FROM dual;
RETURN l_json;
END;
END;
/
JSON機能全体は Oracle JSON完全ガイド、JSONを表形式へ展開する処理は JSON_TABLEでJSONを取り込む方法 が参考になります。
パイプライン関数の戻り値に使う
オブジェクト型は、パイプライン表関数の行型としてよく使われます。複数列を1行のオブジェクトとして返し、それをSQLから表のようにSELECTできます。
CREATE OR REPLACE TYPE sales_row_obj AS OBJECT (
customer_id NUMBER,
total_amt NUMBER
);
/
CREATE OR REPLACE TYPE sales_row_tab AS TABLE OF sales_row_obj;
/
CREATE OR REPLACE FUNCTION get_sales_rows
RETURN sales_row_tab PIPELINED
IS
BEGIN
FOR r IN (
SELECT customer_id, SUM(amount) total_amt
FROM sales
GROUP BY customer_id
) LOOP
PIPE ROW (sales_row_obj(r.customer_id, r.total_amt));
END LOOP;
RETURN;
END;
/
SELECT *
FROM TABLE(get_sales_rows());
パイプライン表関数での使い方は パイプライン表関数完全ガイド や パイプライン関数で大量データ処理を勝たせる完全ガイド と関連します。
メタデータを確認する
作成済みのオブジェクト型は、データディクショナリで確認できます。属性、メソッド、依存関係、コンパイルエラーを確認するSQLを用意しておくと、変更時の調査が楽になります。
SELECT type_name, typecode, attributes, methods, final, instantiable
FROM user_types
WHERE type_name IN ('CUSTOMER_OBJ', 'CUSTOMER_TAB');
SELECT type_name, attr_name, attr_type_name, length, precision, scale
FROM user_type_attrs
WHERE type_name = 'CUSTOMER_OBJ'
ORDER BY attr_no;
SELECT type_name, method_name, method_type, final, instantiable
FROM user_type_methods
WHERE type_name = 'CUSTOMER_OBJ'
ORDER BY method_no;
SELECT name, type, referenced_name, referenced_type
FROM user_dependencies
WHERE referenced_name = 'CUSTOMER_OBJ'
ORDER BY type, name;
SELECT name, type, line, position, text
FROM user_errors
WHERE name = 'CUSTOMER_OBJ'
ORDER BY sequence;
コンパイルエラーや PLS-00302 の確認は PL/SQLコンパイル時エラーと警告の対処、コンポーネント未宣言エラーは PLS-00302の原因と解決方法 が参考になります。
使うべきケース・避けるべきケース
本番前チェックリスト
SELF、NULL、例外、戻り値を明確にしているか。MAP / ORDER を設計したか。NOT FINAL にする必要が本当にあるか。TO_JSON などは自作メソッドとして実装しているか。まとめ
OBJECT TYPE を使うと、Oracle PL/SQLでも属性とメソッドをまとめたオブジェクト指向的な設計ができます。メンバーメソッド、独自コンストラクタ、ネスト表、MAPメソッド、継承を使えば、複雑な値や戻り値を型として表現できます。
一方で、通常の表・レコード・パッケージで十分な場面も多く、オブジェクト列や深い継承は運用を難しくします。実務では、パイプライン関数の行型、API境界の値オブジェクト、比較ロジックを閉じ込めたい型などに絞って使うのが現実的です。

