5.12 レコード変数
次のいずれかの方法でレコード変数を作成できます。
-
RECORD型を定義し、次にその型の変数を宣言します。 -
データベースの表またはビュー内の行の全体または一部を表すレコード変数を宣言するには、
%ROWTYPEを使用します。 -
事前に宣言されているレコード変数と同じ型のレコード変数を宣言するには、
%TYPEを使用します。
(構文およびセマンティクスの詳細は、「レコード変数の宣言」を参照してください。)
ここでのトピック
5.12.1 レコード変数の初期値
RECORD型のレコード変数の場合、型を定義するときに別の初期値を指定しないかぎり、各フィールドの初期値はNULLになります。
%ROWTYPEまたは%TYPEを使用して宣言したレコード変数の場合、各フィールドの初期値はNULLになります。この変数は、参照先項目の初期値を継承しません。
5.12.2 レコード定数の宣言
レコード定数を宣言する場合、初期値をレコードに移入するファンクションを作成して、定数宣言でそのファンクションを起動する必要があります。
例5-35 レコード定数の宣言
この例では、初期値をレコードに移入するファンクションを作成して、定数宣言でそのファンクションを起動します。
Live SQL:
この例は、Oracle Live SQLの「レコード定数の宣言」で表示および実行できます
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 PL/SQL procedure successfully completed.
5.12.3 RECORD型
PL/SQLブロック内に定義されるRECORD型はローカル型です。ブロック内でのみ使用可能であり、スタンドアロン・サブプログラムまたはパッケージ・サブプログラム内にブロックがある場合にのみ、データベースに格納されます。
パッケージ仕様部に定義されるRECORD型はパブリック項目です。パッケージ名(package_name.type_name)で修飾することで、パッケージの外から参照できます。パブリック項目は、DROP PACKAGE文を使用してパッケージを削除するまで、データベースに格納されます。
スキーマ・レベルではRECORD型を宣言できません。したがって、RECORD型はADT属性のデータ型にできません。
RECORD型を定義するには、型の名前とフィールドを指定します。フィールドを定義するには、フィールド名とデータ型を指定します。デフォルトでは、フィールドの初期値はNULLです。フィールドにNOT NULL制約を指定できますが、指定する場合は、初期値にNULL以外の値を指定する必要があります。NOT NULL制約がない場合、NULL以外の初期値はオプションです。
パッケージ仕様部で定義されたRECORD型は、同一定義のローカルRECORD型と互換性がありません。
例5-36 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
例5-37 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
例5-38 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
例5-39 同一定義のパッケージRECORD型とローカルRECORD型
この例では、パッケージpkgと無名ブロックで、同じRECORD型rec_typeを定義します。パッケージでは、rec_typeのパラメータを取るプロシージャprint_rec_typeを定義します。無名ブロックでは、パッケージ型(pkg.rec_type)の変数r1と、ローカル型(rec_type)の変数r2を宣言します。無名ブロックは、r1をprint_rec_typeに渡せますが、r2をprint_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'
5.12.4 %ROWTYPE属性を使用した項目の宣言
%ROWTYPE属性を使用すると、データベースの表またはビュー内の行の全体または一部を表すレコード変数を宣言できます。
構文およびセマンティクスの詳細は、「%ROWTYPE属性」を参照してください。
ここでのトピック
5.12.4.1 常に行の全体を表すレコード変数の宣言
常にデータベースの表またはビュー内の行の全体を表すレコード変数を宣言するには、次の構文を使用します。
variable_name table_or_view_name%ROWTYPE;
このレコードは、表またはビューのすべての列に対して、同じ名前とデータ型のフィールドを持ちます。
関連項目:
%ROWTYPEの詳細は、「%ROWTYPE属性」を参照してください
例5-40 データベース表の行全体を表す%ROWTYPE変数
この例では、表departmentsの行を表すレコード変数を宣言し、フィールドに値を代入してからフィールドを出力します。この例を例5-36と比較してください。
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
例5-41 初期値または制約を継承しない%ROWTYPE変数
この例では、2つの列を持つ表を作成し、それぞれの列に初期値とNOT NULL制約を指定します。次に、この表の行を表すレコード変数を宣言してからフィールドを出力し、初期値またはNOT NULL制約が変数に継承されなかったことを示します。
Live SQL:
この例は、Oracle Live SQLの「初期値または制約を継承しない%ROWTYPE変数」で表示および実行できます
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
5.12.4.2 行の一部を表すことができるレコード変数の宣言
データベースの表またはビュー内の行の一部を表すことができるレコード変数を宣言するには、次の構文を使用します。
variable_name cursor%ROWTYPE;
カーソルは問合せに関連付けられています。問合せにより選択されるすべての列に対して、レコード変数の中には、対応する型互換のフィールドが存在している必要があります。問合せにより表またはビュー内のすべての列が選択される場合、変数は行の全体を表しますが、それ以外の場合は、行の一部を表します。カーソルは、明示カーソルまたは強いカーソル変数のいずれかである必要があります。
関連項目:
-
構文の詳細は、「FETCH文」を参照してください
-
カーソルの詳細は、「カーソルの概要」を参照してください
-
明示カーソルの詳細は、「明示カーソル」を参照してください
-
カーソル変数の詳細は、「カーソル変数」を参照してください
-
結合の詳細は、『Oracle Database SQL言語リファレンス』を参照してください
例5-42 データベース表の行の一部を表す%ROWTYPE変数
この例では、サンプル・スキーマHRのemployees表から列first_name、last_nameおよびphone_numberのみを選択する問合せが指定された明示カーソルを宣言します。次に、カーソルにより選択される各列に対応するフィールドを持つレコード変数を宣言します。この変数はemployeesの行の一部を表します。この例を例5-37と比較してください。
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
例5-43 結合行を表す%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;
/5.12.4.3 %ROWTYPE属性と仮想列
%ROWTYPE属性を使用して、仮想列を持つ表の行全体を表すレコード変数を定義する場合、そのレコードを表に挿入できません。かわりに、仮想列以外の各レコード・フィールドを表に挿入する必要があります。
例5-44 %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
例5-45 %ROWTYPEレコードの表への挿入(正しい例)
これは、仮想列以外の各レコード・フィールドを表に挿入することで、例5-44の問題を解決しています。
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.
5.12.4.4 %ROWTYPE属性と非表示列
%ROWTYPE属性を使用して、非表示列を持つ表の行を表すレコード変数を定義しており、その後、非表示列を表示列に変更するとします。
「行の一部を表すことができるレコード変数の宣言」に示すように、カーソルとともにレコード変数を定義し、その後、非表示列を表示列に変更しても、レコード変数の構造は変わりません。
ただし、常に行の全体を表すレコード変数の宣言に示すようにレコード変数を定義し、SELECT * INTO文を使用してレコードに値を代入した後、非表示列を表示列に変更すると、レコードの構造が変わります。例5-46を参照してください。
関連項目:
非表示列の概要は、『Oracle Database SQL言語リファレンス』を参照してください。
例5-46 非表示列の表示列への変更により影響を受ける%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.