【Oracle】オブジェクト型(CREATE TYPE)完全ガイド|属性・メンバーメソッド・型の継承・MAP ファンクションまで解説

Oracle のオブジェクト型(Object Type)は、SQL レベルで属性(列)とメソッド(関数・プロシージャ)をひとまとめにしたユーザー定義型です。オブジェクト指向の概念(カプセル化・継承・ポリモーフィズム)をSQL・PL/SQL に持ち込むことができます。

コレクション型(VARRAY・ネストした表)の要素型として使ったり、テーブルの列の型として使ったりと、複雑なデータ構造をリレーショナルモデルと組み合わせる際に役立ちます。

この記事でわかること

  • CREATE TYPE / CREATE TYPE BODY でオブジェクト型を定義する方法
  • MEMBER FUNCTION / MEMBER PROCEDURE と STATIC メソッドの違い
  • MAP ファンクションと ORDER ファンクションで比較演算を実装する方法
  • UNDER で型の継承(サブタイプ)を実装する方法
  • TREAT 演算子でスーパータイプからサブタイプにキャストする方法
  • オブジェクト型をテーブル列・コレクション要素として使う実務パターン
スポンサーリンク

オブジェクト型の作成(CREATE TYPE / CREATE TYPE BODY)

オブジェクト型の作成は2ステップです。まず CREATE TYPE で型の仕様(属性とメソッドのシグネチャ)を定義し、次に CREATE TYPE BODY でメソッドの実装を記述します。

CREATE TYPE でオブジェクト型を定義する
-- ステップ1: 型の仕様(属性とメソッドのシグネチャ)を定義する
CREATE OR REPLACE TYPE address_t AS OBJECT (
    street      VARCHAR2(100),
    city        VARCHAR2(50),
    postal_code VARCHAR2(10),

    -- MEMBER FUNCTION: SELF(インスタンス自身)を参照できる
    MEMBER FUNCTION format_address RETURN VARCHAR2,

    -- STATIC FUNCTION: インスタンス不要(クラスメソッドに相当)
    STATIC FUNCTION validate_postal(p_code VARCHAR2) RETURN BOOLEAN,

    -- MAP FUNCTION: 比較演算(ORDER BY・DISTINCT など)に使うキー値を返す
    MAP MEMBER FUNCTION to_sort_key RETURN VARCHAR2
);
/

-- ステップ2: 実装を記述する
CREATE OR REPLACE TYPE BODY address_t AS

    MEMBER FUNCTION format_address RETURN VARCHAR2 IS
    BEGIN
        RETURN SELF.street || ', ' || SELF.city || ' ' || SELF.postal_code;
    END format_address;

    STATIC FUNCTION validate_postal(p_code VARCHAR2) RETURN BOOLEAN IS
    BEGIN
        RETURN REGEXP_LIKE(p_code, '^\d{3}-\d{4}$');   -- 日本の郵便番号形式
    END validate_postal;

    MAP MEMBER FUNCTION to_sort_key RETURN VARCHAR2 IS
    BEGIN
        RETURN SELF.postal_code || SELF.city || SELF.street;
    END to_sort_key;

END;
/
オブジェクト型のインスタンスを作成して使う
-- コンストラクタ(型名をそのまま呼ぶ)でインスタンスを生成する
DECLARE
    v_addr address_t;
    v_valid BOOLEAN;
BEGIN
    -- コンストラクタ: 属性を順番に指定する
    v_addr := address_t(
        street      => '千代田1-1',
        city        => '東京都千代田区',
        postal_code => '100-0001'
    );

    -- MEMBER FUNCTION を呼び出す
    DBMS_OUTPUT.PUT_LINE(v_addr.format_address());
    -- → 千代田1-1, 東京都千代田区 100-0001

    -- STATIC FUNCTION を呼び出す
    v_valid := address_t.validate_postal('100-0001');
    DBMS_OUTPUT.PUT_LINE(CASE WHEN v_valid THEN '有効' ELSE '無効' END);

    -- 属性を変更する(直接代入可能)
    v_addr.city := '東京都新宿区';
    DBMS_OUTPUT.PUT_LINE(v_addr.format_address());
END;
/

-- SQL から直接使う(SELECT でコンストラクタを呼ぶ)
SELECT address_t('丸の内1-1', '東京都千代田区', '100-0005').format_address()
FROM DUAL;

オブジェクト型をテーブルの列として使う

オブジェクト型を列型に使う(列オブジェクト)
-- オブジェクト型を列として持つテーブルを作成する
CREATE TABLE customers (
    customer_id    NUMBER PRIMARY KEY,
    customer_name  VARCHAR2(100),
    home_address   address_t,      -- address_t 型の列(列オブジェクト)
    work_address   address_t
);

-- INSERT: コンストラクタで値を指定する
INSERT INTO customers VALUES (
    1,
    '山田太郎',
    address_t('千代田1-1', '東京都千代田区', '100-0001'),  -- home_address
    address_t('新宿3-1',   '東京都新宿区',   '160-0022')   -- work_address
);
COMMIT;

-- SELECT: ドット記法で属性にアクセスする
SELECT
    customer_name,
    home_address.city         AS home_city,
    home_address.postal_code  AS home_postal
FROM customers;

-- MEMBER FUNCTION を SELECT から呼び出す
SELECT
    customer_name,
    c.home_address.format_address() AS formatted_home   -- テーブル別名が必要
FROM customers c;
-- 注意: オブジェクト型のメソッドを呼ぶ場合はテーブルに別名を付ける必要がある

-- 列オブジェクトの属性で WHERE 句を使う
SELECT customer_name FROM customers
WHERE home_address.city = '東京都千代田区';

型の継承(サブタイプ)

Oracle のオブジェクト型は UNDER キーワードで継承できます。スーパータイプを NOT FINAL に指定する必要があります。

UNDER で型の継承を実装する
-- ベース型(スーパータイプ): NOT FINAL が継承を許可するキーワード
CREATE OR REPLACE TYPE shape_t AS OBJECT (
    color VARCHAR2(20),
    MEMBER FUNCTION area     RETURN NUMBER,  -- サブタイプでオーバーライドする
    MEMBER FUNCTION describe RETURN VARCHAR2
) NOT FINAL;  -- NOT FINAL: サブタイプを作成できる(デフォルトは FINAL で継承不可)
/

CREATE OR REPLACE TYPE BODY shape_t AS
    MEMBER FUNCTION area RETURN NUMBER IS BEGIN RETURN 0; END;
    MEMBER FUNCTION describe RETURN VARCHAR2 IS
    BEGIN RETURN 'Shape: color=' || SELF.color; END;
END;
/

-- サブタイプ: UNDER でスーパータイプを指定する
CREATE OR REPLACE TYPE circle_t UNDER shape_t (
    radius NUMBER,
    OVERRIDING MEMBER FUNCTION area RETURN NUMBER   -- スーパータイプのメソッドをオーバーライド
);
/

CREATE OR REPLACE TYPE BODY circle_t AS
    OVERRIDING MEMBER FUNCTION area RETURN NUMBER IS
    BEGIN
        RETURN 3.14159265 * SELF.radius * SELF.radius;
    END;
END;
/

CREATE OR REPLACE TYPE rectangle_t UNDER shape_t (
    width  NUMBER,
    height NUMBER,
    OVERRIDING MEMBER FUNCTION area RETURN NUMBER
);
/

CREATE OR REPLACE TYPE BODY rectangle_t AS
    OVERRIDING MEMBER FUNCTION area RETURN NUMBER IS
    BEGIN RETURN SELF.width * SELF.height; END;
END;
/

-- スーパータイプ型の変数にサブタイプのインスタンスを代入できる(ポリモーフィズム)
DECLARE
    v_shape   shape_t;
    v_circle  circle_t  := circle_t('red', 5);      -- コンストラクタ: color + radius
    v_rect    rectangle_t := rectangle_t('blue', 4, 6); -- color + width + height
BEGIN
    v_shape := v_circle;
    DBMS_OUTPUT.PUT_LINE('Circle area: ' || v_shape.area());    -- 78.539...(オーバーライド済み)

    v_shape := v_rect;
    DBMS_OUTPUT.PUT_LINE('Rectangle area: ' || v_shape.area()); -- 24
END;
/
TREAT / IS OF で型チェックと型変換をする
-- スーパータイプ型の列を持つテーブルで型別に処理する場合
CREATE TABLE shapes_table (
    shape_id  NUMBER PRIMARY KEY,
    shape     shape_t             -- スーパータイプの列にサブタイプを格納できる
);

INSERT INTO shapes_table VALUES (1, circle_t('red', 5));
INSERT INTO shapes_table VALUES (2, rectangle_t('blue', 4, 6));
INSERT INTO shapes_table VALUES (3, circle_t('green', 3));

-- IS OF: 型を確認する(WHERE 句で使う)
SELECT shape_id FROM shapes_table
WHERE shape IS OF (circle_t);   -- circle_t のインスタンスのみ取得

-- TREAT: スーパータイプからサブタイプにキャストする
SELECT
    shape_id,
    TREAT(shape AS circle_t).radius AS radius    -- circle_t にキャストして radius を取得
FROM   shapes_table
WHERE  shape IS OF (circle_t);
-- circle_t でない行は NULL になる(キャスト失敗では例外を出さない)

まとめ

  • CREATE TYPE / CREATE TYPE BODY:2ステップでオブジェクト型を定義する。仕様と実装を分離することで変更の影響を局所化できる
  • MEMBER / STATIC / MAP メソッド:MEMBER はインスタンスメソッド(SELF で自身を参照)・STATIC はクラスメソッド(インスタンス不要)・MAP はソート比較のキー値を返すファンクション
  • 列オブジェクト:CREATE TABLE でオブジェクト型を列の型として使える。ドット記法で属性にアクセスし、テーブル別名が必要なケースに注意
  • UNDER(継承):スーパータイプに NOT FINAL を指定してサブタイプを作成できる。OVERRIDING MEMBER FUNCTION でメソッドをオーバーライドする
  • TREAT / IS OF:スーパータイプ変数に格納されたインスタンスを型チェックして安全にサブタイプにキャストできる

VARRAY・ネストした表などのコレクション型については Oracle PL/SQL コレクション型完全ガイドを参照してください。オブジェクト型をコレクションの要素型として組み合わせると、複雑なデータ構造を SQL・PL/SQL で扱えます。