レコード変数

次のいずれかの方法でレコード変数を作成できます。

  • RECORD型を定義し、次にその型の変数を宣言します。

  • データベースの表またはビュー内の行の全体または一部を表すレコード変数を宣言するには、%ROWTYPEを使用します。

  • 事前に宣言されているレコード変数と同じ型のレコード変数を宣言するには、%TYPEを使用します。

(構文およびセマンティクスの詳細は、「レコード変数の宣言」を参照してください。)

ここでのトピック

レコード変数の初期値

RECORD型のレコード変数の場合、型を定義するときに別の初期値を指定しないかぎり、各フィールドの初期値はNULLになります。

%ROWTYPEまたは%TYPEを使用して宣言したレコード変数の場合、各フィールドの初期値はNULLになります。この変数は、参照先項目の初期値を継承しません。

レコード定数の宣言

レコード定数を宣言する場合、修飾式の位置表記法または名前関連付け表記法を使用して、値を簡潔な形式で初期化できます。

例6-39 レコード定数の宣言

この例は、修飾式を使用して初期化されるレコード定数rを示しています。0と1の値は、位置表記法を使用して指定されたMy_Rec型マークと集計を明示的に示すことによって割り当てられます。

Live SQL:

この例は、Oracle Live SQLの「レコード定数の宣言」で表示および実行できます


DECLARE
  TYPE My_Rec IS RECORD (a NUMBER, b NUMBER);
  r CONSTANT My_Rec := My_Rec(0,1);
BEGIN
  DBMS_OUTPUT.PUT_LINE('r.a = ' || r.a);
  DBMS_OUTPUT.PUT_LINE('r.b = ' || r.b);
END;
/

Oracle Databaseリリース18cより前では、同じ結果を得るために、初期値をレコードに移入するファンクションを使用してレコード定数を宣言し、定数宣言でファンクションを起動する必要がありました。両方の例を比較すると、修飾式によってよりコンパクトにできることで、プログラムの明確性と開発者の生産性が向上することがわかります。

CREATE OR REPLACE PACKAGE My_Types AUTHID CURRENT_USER IS
  TYPE My_Rec IS RECORD (a NUMBER, b NUMBER);
  FUNCTION Init_My_Rec RETURN My_Rec;
END My_Types;
/
CREATE OR REPLACE PACKAGE BODY My_Types IS
  FUNCTION Init_My_Rec RETURN My_Rec IS
    Rec My_Rec;
  BEGIN
    Rec.a := 0;
    Rec.b := 1;
    RETURN Rec;
  END Init_My_Rec;
END My_Types;
/
DECLARE
  r CONSTANT My_Types.My_Rec := My_Types.Init_My_Rec();
BEGIN
  DBMS_OUTPUT.PUT_LINE('r.a = ' || r.a);
  DBMS_OUTPUT.PUT_LINE('r.b = ' || r.b);
END;
/

結果:

r.a = 0
r.b = 1

例6-40 レコード定数の宣言

この例は、位置表記法を使用して修飾式で初期化されたレコード定数c_smallを示しています。c_largeレコード定数は、名前関連付け表記法を使用して修飾式で初期化されます。

DECLARE
  TYPE t_size IS RECORD (x NUMBER, y NUMBER);
  c_small  CONSTANT t_size := t_size(32,36);
  c_large  CONSTANT t_size := t_size(x => 192, y => 292);
BEGIN
  DBMS_OUTPUT.PUT_LINE('Small size is ' || c_small.x  || ' by ' || c_small.y);
  DBMS_OUTPUT.PUT_LINE('Large size is ' || c_large.x  || ' by ' || c_large.y);
END;
/

結果:

Small size is 32 by 36
Large size is 192 by 292

RECORD型

PL/SQLブロック内に定義されるRECORD型はローカル型です。ブロック内でのみ使用可能であり、スタンドアロン・サブプログラムまたはパッケージ・サブプログラム内にブロックがある場合にのみ、データベースに格納されます。

パッケージ仕様部に定義されるRECORD型はパブリック項目です。パッケージ名(package_name.type_name)で修飾することで、パッケージの外から参照できます。パブリック項目は、DROP PACKAGE文を使用してパッケージを削除するまで、データベースに格納されます。

スキーマ・レベルではRECORD型を宣言できません。したがって、RECORD型はADT属性のデータ型にできません。

RECORD型を定義するには、型の名前とフィールドを指定します。フィールドを定義するには、フィールド名とデータ型を指定します。デフォルトでは、フィールドの初期値はNULLです。フィールドにNOT NULL制約を指定できますが、指定する場合は、初期値にNULL以外の値を指定する必要があります。NOT NULL制約がない場合、NULL以外の初期値はオプションです。

パッケージ仕様部で定義されたRECORD型は、同一定義のローカルRECORD型と互換性がありません。

例6-41 RECORD型の定義および変数の宣言

この例では、DeptRecTypという名前のRECORD型を定義し、各フィールドに初期値を指定します。その後、dept_recという名前でその型の変数を宣言し、変数のフィールドを出力します。

Live SQL:

この例は、Oracle Live SQLの「RECORD型の定義および変数の宣言」で表示および実行できます

DECLARE
  TYPE DeptRecTyp IS RECORD (
    dept_id    NUMBER(4) NOT NULL := 10,
    dept_name  VARCHAR2(30) NOT NULL := 'Administration',
    mgr_id     NUMBER(6) := 200,
    loc_id     NUMBER(4) := 1700
  );
 
  dept_rec DeptRecTyp;
BEGIN
  DBMS_OUTPUT.PUT_LINE('dept_id:   ' || dept_rec.dept_id);
  DBMS_OUTPUT.PUT_LINE('dept_name: ' || dept_rec.dept_name);
  DBMS_OUTPUT.PUT_LINE('mgr_id:    ' || dept_rec.mgr_id);
  DBMS_OUTPUT.PUT_LINE('loc_id:    ' || dept_rec.loc_id);
END;
/

結果:

dept_id:   10
dept_name: Administration
mgr_id:    200
loc_id:    1700

例6-42 RECORDフィールドを持つRECORD型(ネストされたレコード)

この例では、name_recおよびcontactという2つのRECORD型を定義します。型contactには型name_recのフィールドを含めます。

Live SQL:

この例は、Oracle Live SQLの「RECORDフィールドを持つRECORD型(ネストされたレコード)」で表示および実行できます

DECLARE
  TYPE name_rec IS RECORD (
    first  employees.first_name%TYPE,
    last   employees.last_name%TYPE
  );
 
  TYPE contact IS RECORD (
    name  name_rec,                    -- nested record
    phone employees.phone_number%TYPE
  );
 
  friend contact;
BEGIN
  friend.name.first := 'John';
  friend.name.last := 'Smith';
  friend.phone := '1-650-555-1234';
  
  DBMS_OUTPUT.PUT_LINE (
    friend.name.first  || ' ' ||
    friend.name.last   || ', ' ||
    friend.phone
  );
END;
/

結果:

John Smith, 1-650-555-1234

例6-43 VARRAYフィールド持つRECORD型

この例では、full_nameというVARRAY型と、contactというRECORD型を定義します。型contactには型full_nameのフィールドを含めます。

Live SQL:

この例は、Oracle Live SQLの「VARRAYフィールド持つRECORD型」で表示および実行できます

DECLARE
  TYPE full_name IS VARRAY(2) OF VARCHAR2(20);
 
  TYPE contact IS RECORD (
    name  full_name := full_name('John', 'Smith'),  -- varray field
    phone employees.phone_number%TYPE
  );
 
  friend contact;
BEGIN
  friend.phone := '1-650-555-1234';
  
  DBMS_OUTPUT.PUT_LINE (
    friend.name(1) || ' ' ||
    friend.name(2) || ', ' ||
    friend.phone
  );
END;
/

結果:

John Smith, 1-650-555-1234

例6-44 同一定義のパッケージRECORD型とローカルRECORD型

この例では、パッケージpkgと無名ブロックで、同じRECORDrec_typeを定義します。パッケージでは、rec_typeのパラメータを取るプロシージャprint_rec_typeを定義します。無名ブロックでは、パッケージ型(pkg.rec_type)の変数r1と、ローカル型(rec_type)の変数r2を宣言します。無名ブロックは、r1print_rec_typeに渡せますが、r2print_rec_typeに渡すことはできません。

Live SQL:

この例は、Oracle Live SQLの「同一定義のパッケージRECORD型とローカルRECORD型」で表示および実行できます

CREATE OR REPLACE PACKAGE pkg AS
  TYPE rec_type IS RECORD (       -- package RECORD type
    f1 INTEGER,
    f2 VARCHAR2(4)
  );
  PROCEDURE print_rec_type (rec rec_type);
END pkg;
/
CREATE OR REPLACE PACKAGE BODY pkg AS
  PROCEDURE print_rec_type (rec rec_type) IS
  BEGIN
    DBMS_OUTPUT.PUT_LINE(rec.f1);
    DBMS_OUTPUT.PUT_LINE(rec.f2);
  END; 
END pkg;
/
DECLARE
  TYPE rec_type IS RECORD (       -- local RECORD type
    f1 INTEGER,
    f2 VARCHAR2(4)
  );
  r1 pkg.rec_type;                -- package type
  r2     rec_type;                -- local type
 
BEGIN
  r1.f1 := 10; r1.f2 := 'abcd';
  r2.f1 := 25; r2.f2 := 'wxyz';
 
  pkg.print_rec_type(r1);  -- succeeds
  pkg.print_rec_type(r2);  -- fails
END;
/

結果:

  pkg.print_rec_type(r2);  -- fails
  *
ERROR at line 14:
ORA-06550: line 14, column 3:
PLS-00306: wrong number or types of arguments in call to 'PRINT_REC_TYPE'

%ROWTYPE属性を使用した項目の宣言

%ROWTYPE属性を使用すると、データベースの表またはビュー内の行の全体または一部を表すレコード変数を宣言できます。

構文およびセマンティクスの詳細は、「%ROWTYPE属性」を参照してください。

ここでのトピック

常に行の全体を表すレコード変数の宣言

常にデータベースの表またはビュー内の行の全体を表すレコード変数を宣言するには、次の構文を使用します。

variable_name table_or_view_name%ROWTYPE;

このレコードは、表またはビューのすべての列に対して、同じ名前とデータ型のフィールドを持ちます。

関連項目:

%ROWTYPEの詳細は、「%ROWTYPE属性」を参照してください

例6-45 データベース表の行全体を表す%ROWTYPE変数

この例では、表departmentsの行を表すレコード変数を宣言し、フィールドに値を代入してからフィールドを出力します。この例を例6-41と比較してください。

Live SQL:

この例は、Oracle Live SQLの「データベース表の行全体を表す%ROWTYPE変数」で表示および実行できます

DECLARE
  dept_rec departments%ROWTYPE;
BEGIN
  -- Assign values to fields:
  
  dept_rec.department_id   := 10;
  dept_rec.department_name := 'Administration';
  dept_rec.manager_id      := 200;
  dept_rec.location_id     := 1700;
 
  -- Print fields:
 
  DBMS_OUTPUT.PUT_LINE('dept_id:   ' || dept_rec.department_id);
  DBMS_OUTPUT.PUT_LINE('dept_name: ' || dept_rec.department_name);
  DBMS_OUTPUT.PUT_LINE('mgr_id:    ' || dept_rec.manager_id);
  DBMS_OUTPUT.PUT_LINE('loc_id:    ' || dept_rec.location_id);
END;
/

結果:

dept_id:   10
dept_name: Administration
mgr_id:    200
loc_id:    1700

例6-46 初期値または制約を継承しない%ROWTYPE変数

この例では、2つの列を持つ表を作成し、それぞれの列に初期値とNOT NULL制約を指定します。次に、この表の行を表すレコード変数を宣言してからフィールドを出力し、初期値またはNOT NULL制約が変数に継承されなかったことを示します。

Live SQL:

この例は、Oracle Live SQLの「初期値または制約を継承しない%ROWTYPE変数」で表示および実行できます

CREATE OR REPLACE PROCEDURE print (n INTEGER) IS 
BEGIN 
  IF n IS NOT NULL THEN 
    DBMS_OUTPUT.PUT_LINE(n); 
  ELSE 
    DBMS_OUTPUT.PUT_LINE('NULL'); 
  END IF; 
END print; 
/

DROP TABLE t1;
CREATE TABLE t1 (
  c1 INTEGER DEFAULT 0 NOT NULL,
  c2 INTEGER DEFAULT 1 NOT NULL
);
 
DECLARE
  t1_row t1%ROWTYPE;
BEGIN
  DBMS_OUTPUT.PUT('t1.c1 = ');
  DBMS_OUTPUT.PUT_LINE(NVL(TO_CHAR(t1_row.c1), 'NULL'));

  DBMS_OUTPUT.PUT('t1.c2 = '); print(t1_row.c2);
  DBMS_OUTPUT.PUT_LINE(NVL(TO_CHAR(t1_row.c2), 'NULL'));
END;
/

結果:

t1.c1 = NULL
t1.c2 = NULL

行の一部を表すことができるレコード変数の宣言

データベースの表またはビュー内の行の一部を表すことができるレコード変数を宣言するには、次の構文を使用します。

variable_name cursor%ROWTYPE;

カーソルは問合せに関連付けられています。問合せにより選択されるすべての列に対して、レコード変数の中には、対応する型互換のフィールドが存在している必要があります。問合せにより表またはビュー内のすべての列が選択される場合、変数は行の全体を表しますが、それ以外の場合は、行の一部を表します。カーソルは、明示カーソルまたは強いカーソル変数のいずれかである必要があります。

関連項目:

例6-47 データベース表の行の一部を表す%ROWTYPE変数

この例では、サンプル・スキーマHRemployees表から列first_namelast_nameおよびphone_numberのみを選択する問合せが指定された明示カーソルを宣言します。次に、カーソルにより選択される各列に対応するフィールドを持つレコード変数を宣言します。この変数はemployeesの行の一部を表します。この例を例6-42と比較してください。

Live SQL:

この例は、Oracle Live SQLの「データベース表の行の一部を表す%ROWTYPE変数」で表示および実行できます

DECLARE
  CURSOR c IS
    SELECT first_name, last_name, phone_number
    FROM employees;
 
  friend c%ROWTYPE;
BEGIN
  friend.first_name   := 'John';
  friend.last_name    := 'Smith';
  friend.phone_number := '1-650-555-1234';
  
  DBMS_OUTPUT.PUT_LINE (
    friend.first_name  || ' ' ||
    friend.last_name   || ', ' ||
    friend.phone_number
  );
END;
/

結果:

John Smith, 1-650-555-1234

例6-48 結合行を表す%ROWTYPE変数

この例では、結合問合せが指定された明示カーソルを定義した後、このカーソルにより選択される各列に対応するフィールドを持つレコード変数を宣言します。

Live SQL:

この例は、Oracle Live SQLの「結合行を表す%ROWTYPE変数」で表示および実行できます

DECLARE
  CURSOR c2 IS
    SELECT employee_id, email, employees.manager_id, location_id
    FROM employees, departments
    WHERE employees.department_id = departments.department_id;
  
  join_rec c2%ROWTYPE;  -- includes columns from two tables
  
BEGIN
  NULL;
END;
/

%ROWTYPE属性と仮想列

%ROWTYPE属性を使用して、仮想列を持つ表の行全体を表すレコード変数を定義する場合、そのレコードを表に挿入できません。かわりに、仮想列以外の各レコード・フィールドを表に挿入する必要があります。

例6-49 %ROWTYPEレコードの表への挿入(間違った例)

この例では、仮想列を持つ表の行全体を表すレコード変数を作成し、レコードに移入してから、そのレコードを表に挿入した結果、ORA-54013エラーが発生します。

DROP TABLE plch_departure;
 
CREATE TABLE plch_departure (
  destination    VARCHAR2(100),
  departure_time DATE,
  delay          NUMBER(10),
  expected       GENERATED ALWAYS AS (departure_time + delay/24/60/60)
);
 
 
DECLARE
 dep_rec plch_departure%ROWTYPE;
BEGIN
  dep_rec.destination := 'X'; 
  dep_rec.departure_time := SYSDATE;
  dep_rec.delay := 1500;
 
  INSERT INTO plch_departure VALUES dep_rec;
END;
/

結果:

DECLARE
*
ERROR at line 1:
ORA-54013: INSERT operation disallowed on virtual columns
ORA-06512: at line 8

例6-50 %ROWTYPEレコードの表への挿入(正しい例)

これは、仮想列以外の各レコード・フィールドを表に挿入することで、例6-49の問題を解決しています。

DECLARE
  dep_rec plch_departure%rowtype;
BEGIN
  dep_rec.destination := 'X';
  dep_rec.departure_time := SYSDATE;
  dep_rec.delay := 1500;
 
  INSERT INTO plch_departure (destination, departure_time, delay)
  VALUES (dep_rec.destination, dep_rec.departure_time, dep_rec.delay);
end;
/

結果:

PL/SQL procedure successfully completed.

%ROWTYPE属性と非表示列

%ROWTYPE属性を使用して、非表示列を持つ表の行を表すレコード変数を定義しており、その後、非表示列を表示列に変更するとします。

「行の一部を表すことができるレコード変数の宣言」に示すように、カーソルとともにレコード変数を定義し、その後、非表示列を表示列に変更しても、レコード変数の構造は変わりません。

ただし、常に行の全体を表すレコード変数の宣言に示すようにレコード変数を定義し、SELECT * INTO文を使用してレコードに値を代入した後、非表示列を表示列に変更すると、レコードの構造が変わります。例6-51を参照してください。

関連項目:

非表示列の概要は、『Oracle Database SQL言語リファレンス』を参照してください。

例6-51 非表示列の表示列への変更により影響を受ける%ROWTYPE

CREATE TABLE t (a INT, b INT, c INT INVISIBLE);
INSERT INTO t (a, b, c) VALUES (1, 2, 3);
COMMIT;

DECLARE
  t_rec t%ROWTYPE;  -- t_rec has fields a and b, but not c
BEGIN
  SELECT * INTO t_rec FROM t WHERE ROWNUM < 2;  -- t_rec(a)=1, t_rec(b)=2
  DBMS_OUTPUT.PUT_LINE('c = ' || t_rec.c);
END;
/

結果:

  DBMS_OUTPUT.PUT_LINE('c = ' || t_rec.c);
                                       *
ERROR at line 5:
ORA-06550: line 5, column 40:
PLS-00302: component 'C' must be declared
ORA-06550: line 5, column 3:
PL/SQL: Statement ignored
 

非表示列を表示列に変更:

ALTER TABLE t MODIFY (c VISIBLE);
 

結果:

Table altered.
 

前述の無名ブロックを繰り返す:

DECLARE
  t_rec t%ROWTYPE;  -- t_rec has fields a, b, and c
BEGIN
  SELECT * INTO t_rec FROM t WHERE ROWNUM < 2;  -- t_rec(a)=1, t_rec(b)=2,
                                                  -- t_rec(c)=3
  DBMS_OUTPUT.PUT_LINE('c = ' || t_rec.c);
END;
/

結果:

c = 3
 
PL/SQL procedure successfully completed.