サブプログラムのパラメータ
サブプログラムにパラメータが含まれている場合は、起動のたびにパラメータ値を変えることができます。
ここでのトピック
サブプログラムの仮パラメータと実パラメータ
サブプログラムにパラメータを保持する場合、サブプログラムのヘッダーで仮パラメータを宣言します。仮パラメータの宣言ごとに、パラメータの名前とデータ型を指定し、オプションでそのモードとデフォルト値を指定します。サブプログラムの実行部で、仮パラメータをその名前によって参照します。
サブプログラムを起動する際に、仮パラメータに代入する値が含まれる実パラメータを指定します。対応する実パラメータと仮パラメータのデータ型には、互換性が必要です。
ノート:
次のように、制約付きサブタイプの仮パラメータを宣言できます。
DECLARE SUBTYPE n1 IS NUMBER(1); SUBTYPE v1 IS VARCHAR2(1); PROCEDURE p (n n1, v v1) IS ...
ただし、次のように、仮パラメータの宣言内に制約を含めることはできません。
DECLARE PROCEDURE p (n NUMBER(1), v VARCHAR2(1)) IS ...
ヒント:
混同を避けるため、仮パラメータと実パラメータには異なる名前を使用してください。
ノート:
-
実際のパラメータ(仮パラメータのデフォルト値を含む)はどのような順序でも評価できます。プログラムにより評価の順序が決定される場合は、プログラムで決定が行われる時点で動作は定義されません。
-
サーバー間リモート・プロシージャ・コール(RPC)ではLOBパラメータは使用できません。
例9-9では、プロシージャに仮パラメータのemp_id
およびamount
が含まれます。プロシージャの最初の起動では、対応する実パラメータはemp_num
(値120)およびbonus
(値100)です。プロシージャの2回目の起動では、実パラメータはemp_num
(値120)およびmerit
+ bonus
(値150)です。
トピック:
関連項目:
-
仮パラメータの宣言の構文およびセマンティクスは、「仮パラメータの宣言」を参照してください
-
ファンクションの起動の構文およびセマンティクスは、「function_call ::=」および「function_call」を参照してください
-
プロシージャの起動の構文およびセマンティクスは、procedure_call ::=およびprocedureを参照してください
例9-9 仮パラメータと実パラメータ
DECLARE emp_num NUMBER(6) := 120; bonus NUMBER(6) := 100; merit NUMBER(4) := 50; PROCEDURE raise_salary ( emp_id NUMBER, -- formal parameter amount NUMBER -- formal parameter ) IS BEGIN UPDATE employees SET salary = salary + amount -- reference to formal parameter WHERE employee_id = emp_id; -- reference to formal parameter END raise_salary; BEGIN raise_salary(emp_num, bonus); -- actual parameters /* raise_salary runs this statement: UPDATE employees SET salary = salary + 100 WHERE employee_id = 120; */ raise_salary(emp_num, merit + bonus); -- actual parameters /* raise_salary runs this statement: UPDATE employees SET salary = salary + 150 WHERE employee_id = 120; */ END; /
制約付きサブタイプの仮パラメータ
仮パラメータのデータ型が制約付きサブタイプの場合、次のことに注意してください。
-
サブタイプに
NOT
NULL
制約が含まれる場合、実パラメータはその制約を継承します。 -
サブタイプにベース型
VARCHAR2
が含まれる場合、実パラメータはサブタイプのサイズを継承しません。 -
サブタイプに数値ベース型が含まれる場合、実パラメータはサブタイプの範囲を継承しますが、精度または位取りは継承しません。
ノート:
ファンクションでは、RETURN
datatype
句で非表示仮パラメータを宣言し、RETURN
value
文で対応する実パラメータを指定します。したがって、datatype
が制約付きデータ型の場合は、前述の規則がvalue
に適用されます(例9-11を参照)。
例9-10で、サブプログラムの実パラメータは、NOT
NULL
制約を継承していますが、VARCHAR2
サブタイプのサイズは継承していません。
「PL/SQLの事前定義のデータ型」に示されているように、PL/SQLには、他のデータ型の制約付きサブタイプである多くの事前定義のデータ型が用意されています。たとえば、INTEGER
はNUMBER
の制約付きサブタイプです。
SUBTYPE INTEGER IS NUMBER(38,0);
例9-11では、ファンクションに仮パラメータINTEGER
と戻り値INTEGER
の両方が含まれています。無名ブロックは、整数でない実パラメータを使用してファンクションを起動します。実パラメータはINTEGER
の範囲を継承しますが、精度と位取りは継承せず、また実パラメータはINTEGER
の範囲内にあるため、起動が成功します。同じ理由で、RETURN
文は正常に非整数値を戻します。
例9-12では、ファンクションは仮パラメータを戻す前に、その仮パラメータを制約付きサブタイプINTEGER
に暗黙的に変換しています。
関連項目:
制約付きサブタイプの一般情報は、「制約付きサブタイプ」を参照してください
例9-10 サブタイプからNOT NULLのみを継承する実パラメータ
DECLARE SUBTYPE License IS VARCHAR2(7) NOT NULL; n License := 'DLLLDDD'; PROCEDURE p (x License) IS BEGIN DBMS_OUTPUT.PUT_LINE(x); END; BEGIN p('1ABC123456789'); -- Succeeds; size is not inherited p(NULL); -- Raises error; NOT NULL is inherited END; /
結果:
p(NULL); -- Raises error; NOT NULL is inherited * ERROR at line 12: ORA-06550: line 12, column 5: PLS-00567: cannot pass NULL to a NOT NULL constrained formal parameter ORA-06550: line 12, column 3: PL/SQL: Statement ignored
例9-11 サブタイプから範囲のみを継承する実パラメータと戻り値
DECLARE
FUNCTION test (p INTEGER) RETURN INTEGER IS
BEGIN
DBMS_OUTPUT.PUT_LINE('p = ' || p);
RETURN p;
END test;
BEGIN
DBMS_OUTPUT.PUT_LINE('test(p) = ' || test(0.66));
END;
/
結果:
p = .66 test(p) = .66 PL/SQL procedure successfully completed.
例9-12 ファンクションによる仮パラメータから制約付きサブタイプへの暗黙的な変換
DECLARE FUNCTION test (p NUMBER) RETURN NUMBER IS q INTEGER := p; -- Implicitly converts p to INTEGER BEGIN DBMS_OUTPUT.PUT_LINE('p = ' || q); -- Display q, not p RETURN q; -- Return q, not p END test; BEGIN DBMS_OUTPUT.PUT_LINE('test(p) = ' || test(0.66)); END; /
結果:
p = 1 test(p) = 1 PL/SQL procedure successfully completed.
サブプログラムのパラメータの引渡し方法
PL/SQLコンパイラには、実パラメータをサブプログラムに引き渡す方法として次の2つがあります。
-
参照渡し
コンパイラは、実パラメータを指すポインタをサブプログラムに渡します。実パラメータと仮パラメータは、同じメモリー位置を参照します。
-
値渡し
コンパイラは、実パラメータの値を対応する仮パラメータに代入します。実パラメータと仮パラメータは、異なるメモリー位置を参照します。
コンパイラは、必要に応じて実パラメータのデータ型を仮パラメータのデータ型に暗黙的に変換します。暗黙的なデータ変換の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。
ヒント:
(『Oracle Database SQL言語リファレンス』で説明されている理由のために)暗黙的なデータ変換を回避するには、次のいずれかの方法を使用します。
-
実パラメータとして使用する予定の変数を、それに対応する仮パラメータと同じデータ型で宣言します(例9-13の変数
x
の宣言を参照)。 -
『Oracle Database SQL言語リファレンス』で説明されているSQL変換ファンクションを使用して、実パラメータをそれに対応する仮パラメータのデータ型に明示的に変換します(例9-13の3回目のプロシージャの起動を参照)。
-
例9-13では、プロシージャp
に、値によって渡される1つのパラメータn
が含まれています。無名ブロックは、p
を3回起動していますが、そのうち2回は暗黙的な変換を回避しています。
コンパイラで特定の実パラメータが渡される方法は、そのモードによって異なります(「サブプログラムのパラメータ・モード」を参照)。
例9-13 実パラメータの暗黙的な変換の回避
CREATE OR REPLACE PROCEDURE p ( n NUMBER ) AUTHID DEFINER IS BEGIN NULL; END; / DECLARE x NUMBER := 1; y VARCHAR2(1) := '1'; BEGIN p(x); -- No conversion needed p(y); -- z implicitly converted from VARCHAR2 to NUMBER p(TO_NUMBER(y)); -- z explicitly converted from VARCHAR2 to NUMBER END; /
サブプログラムのパラメータ・モード
仮パラメータのモードによって、その動作が決定されます。
表9-1に、サブプログラムのパラメータ・モードの特性の概要および比較を示します。
表9-1 PL/SQLサブプログラムのパラメータ・モード
パラメータ・モード | デフォルト | ロール |
---|---|---|
IN |
デフォルト・モード |
サブプログラムに値を渡します。 |
OUT |
指定する必要があります。 |
起動元に値を戻します。 |
IN OUT |
指定する必要があります。 |
サブプログラムに初期値を渡し、更新された値を起動元に戻します。 |
表9-2 PL/SQLサブプログラムのパラメータ・モードの特性
パラメータ・モード | 仮パラメータ | 実パラメータ | 参照渡し |
---|---|---|---|
IN |
仮パラメータは定数のように動作します。サブプログラムが起動すると、その値は実パラメータまたはデフォルト値のいずれかの値となり、サブプログラムではその値を変更できません。 |
実パラメータには定数、リテラル、初期化された変数または式が使用できます。 |
実パラメータは参照によって渡されます。 |
OUT |
仮パラメータはその型のデフォルト値に初期化されます。 サブプログラムが起動すると、実パラメータの値にかかわらず、仮パラメータの値は初期値になります。サブプログラムで仮パラメータに値を割り当てることをお薦めします。 |
仮パラメータの型のデフォルト値が |
デフォルトでは、実パラメータは値によって渡されます。 |
IN OUT |
仮パラメータは初期化された変数のように動作します(サブプログラムが起動すると、その値は実パラメータの値になります)。サブプログラムで値を更新することをお薦めします。 |
実パラメータは、変数である必要があります(通常、文字列バッファまたは数値アキュムレータです)。 |
デフォルトでは、実パラメータは値によって渡されます(どちらの向きに対しても)。 |
ヒント:
ファンクションのパラメータには、OUT
およびIN
OUT
を使用しないでください。ファンクションは、0(ゼロ)個以上のパラメータを取り、単一の値を戻すことが理想です。IN
OUT
パラメータを持つファンクションは、複数の値を戻すため、副作用が発生します。
ノート:
Oracle Database提供の多くのパッケージと型の仕様部では、次の表記法を使用して仮パラメータを宣言します。
i1 IN VARCHAR2 CHARACTER SET ANY_CS i2 IN VARCHAR2 CHARACTER SET i1%CHARSET
独自の仮パラメータまたは実パラメータを宣言する場合は、この表記法を使用しないでください。これは、提供されているパッケージ・タイプのOracle実装のために予約されています。
OUT
またはIN
OUT
パラメータが渡される方法にかかわらず、次のようになります。
-
サブプログラムが正常に終了した場合、実パラメータの値が、仮パラメータに代入される最終的な値になります。(仮パラメータには、少なくとも1つの値(初期値)が代入されます。)
-
サブプログラムが例外によって終了した場合、実パラメータの値は定義されません。
-
OUT
およびIN
OUT
仮パラメータは任意の順序で返されます。次の例では、x
およびy
の最終的な値は定義されていません。CREATE OR REPLACE PROCEDURE p (x OUT INTEGER, y OUT INTEGER) AS BEGIN x := 17; y := 93; END; /
OUT
またはIN
OUT
パラメータが参照によって渡される場合、実パラメータと仮パラメータは同じメモリー位置を参照します。そのため、サブプログラムによって仮パラメータの値が変更されると、その変更は実パラメータに即座に反映されます(「参照渡しパラメータによるサブプログラムのパラメータのエイリアシング」を参照)。
例9-14では、プロシージャp
に2つのIN
パラメータ、1つのOUT
パラメータおよび1つのIN
OUT
パラメータが含まれます。OUT
パラメータとIN
OUT
パラメータは、値によって渡されます(デフォルト)。無名ブロックは、異なる実パラメータを使用してp
を2回起動しています。各起動の前に、無名ブロックは実パラメータの値を出力します。プロシージャp
は、仮パラメータの初期値を出力します。各起動の後にも、無名ブロックは実パラメータの値を出力します。
例9-15では、無名ブロックがプロシージャp
(例9-14を参照)を起動していますが、その実パラメータによってp
で事前定義の例外ZERO_DIVIDE
が呼び出されます(p
はこの例外を処理しません)。例外は無名ブロックに伝播され、無名ブロックは、ZERO_DIVIDE
を処理し、p
のIN
およびIN
OUT
パラメータの実パラメータが起動前に保持していた値を現在も維持していることを示します。(例外の伝播の詳細は、「例外の伝播」を参照してください。)
例9-16では、プロシージャp
に3つのOUT
仮パラメータがあります(NULL
以外のデフォルト値を持つレコード型のx
、NULL
以外のデフォルト値を持たないレコード型のy
、およびレコードではないz
)。
x
、y
およびz
の対応する実パラメータはそれぞれ、r1
、r2
およびs
です。s
は初期値を使用して宣言されます。ただし、p
が起動されると、s
の値はNULL
に初期化されます。r1
およびr2
の値は、それらのレコード型のデフォルト値(それぞれabcde
、NULL
)に初期化されます。
例9-14 プロシージャの起動前、起動中および起動後のパラメータ値
CREATE OR REPLACE PROCEDURE p ( a PLS_INTEGER, -- IN by default b IN PLS_INTEGER, c OUT PLS_INTEGER, d IN OUT BINARY_FLOAT ) AUTHID DEFINER IS BEGIN -- Print values of parameters: DBMS_OUTPUT.PUT_LINE('Inside procedure p:'); DBMS_OUTPUT.PUT('IN a = '); DBMS_OUTPUT.PUT_LINE(NVL(TO_CHAR(a), 'NULL')); DBMS_OUTPUT.PUT('IN b = '); DBMS_OUTPUT.PUT_LINE(NVL(TO_CHAR(b), 'NULL')); DBMS_OUTPUT.PUT('OUT c = '); DBMS_OUTPUT.PUT_LINE(NVL(TO_CHAR(c), 'NULL')); DBMS_OUTPUT.PUT_LINE('IN OUT d = ' || TO_CHAR(d)); -- Can reference IN parameters a and b, -- but cannot assign values to them. c := a+10; -- Assign value to OUT parameter d := 10/b; -- Assign value to IN OUT parameter END; / DECLARE aa CONSTANT PLS_INTEGER := 1; bb PLS_INTEGER := 2; cc PLS_INTEGER := 3; dd BINARY_FLOAT := 4; ee PLS_INTEGER; ff BINARY_FLOAT := 5; BEGIN DBMS_OUTPUT.PUT_LINE('Before invoking procedure p:'); DBMS_OUTPUT.PUT('aa = '); DBMS_OUTPUT.PUT_LINE(NVL(TO_CHAR(aa), 'NULL')); DBMS_OUTPUT.PUT('bb = '); DBMS_OUTPUT.PUT_LINE(NVL(TO_CHAR(bb), 'NULL')); DBMS_OUTPUT.PUT('cc = '); DBMS_OUTPUT.PUT_LINE(NVL(TO_CHAR(cc), 'NULL')); DBMS_OUTPUT.PUT_LINE('dd = ' || TO_CHAR(dd)); p (aa, -- constant bb, -- initialized variable cc, -- initialized variable dd -- initialized variable ); DBMS_OUTPUT.PUT_LINE('After invoking procedure p:'); DBMS_OUTPUT.PUT('aa = '); DBMS_OUTPUT.PUT_LINE(NVL(TO_CHAR(aa), 'NULL')); DBMS_OUTPUT.PUT('bb = '); DBMS_OUTPUT.PUT_LINE(NVL(TO_CHAR(bb), 'NULL')); DBMS_OUTPUT.PUT('cc = '); DBMS_OUTPUT.PUT_LINE(NVL(TO_CHAR(cc), 'NULL')); DBMS_OUTPUT.PUT_LINE('dd = ' || TO_CHAR(dd)); DBMS_OUTPUT.PUT_LINE('Before invoking procedure p:'); DBMS_OUTPUT.PUT('ee = '); DBMS_OUTPUT.PUT_LINE(NVL(TO_CHAR(ee), 'NULL')); DBMS_OUTPUT.PUT_LINE('ff = ' || TO_CHAR(ff)); p (1, -- literal (bb+3)*4, -- expression ee, -- uninitialized variable ff -- initialized variable ); DBMS_OUTPUT.PUT_LINE('After invoking procedure p:'); DBMS_OUTPUT.PUT('ee = '); DBMS_OUTPUT.PUT_LINE(NVL(TO_CHAR(ee), 'NULL')); DBMS_OUTPUT.PUT_LINE('ff = ' || TO_CHAR(ff)); END; /
結果:
Before invoking procedure p: aa = 1 bb = 2 cc = 3 dd = 4.0E+000 Inside procedure p: IN a = 1 IN b = 2 OUT c = NULL IN OUT d = 4.0E+000 After invoking procedure p: aa = 1 bb = 2 cc = 11 dd = 5.0E+000 Before invoking procedure p: ee = NULL ff = 5.0E+000 Inside procedure p: IN a = 1 IN b = 20 OUT c = NULL IN OUT d = 5.0E+000 After invoking procedure p: ee = 11 ff = 5.0E-001 PL/SQL procedure successfully completed.
例9-15 例外処理後のOUTおよびIN OUTパラメータの値
DECLARE j PLS_INTEGER := 10; k BINARY_FLOAT := 15; BEGIN DBMS_OUTPUT.PUT_LINE('Before invoking procedure p:'); DBMS_OUTPUT.PUT('j = '); DBMS_OUTPUT.PUT_LINE(NVL(TO_CHAR(j), 'NULL')); DBMS_OUTPUT.PUT_LINE('k = ' || TO_CHAR(k)); p(4, 0, j, k); -- causes p to exit with exception ZERO_DIVIDE EXCEPTION WHEN ZERO_DIVIDE THEN DBMS_OUTPUT.PUT_LINE('After invoking procedure p:'); DBMS_OUTPUT.PUT('j = '); DBMS_OUTPUT.PUT_LINE(NVL(TO_CHAR(j), 'NULL')); DBMS_OUTPUT.PUT_LINE('k = ' || TO_CHAR(k)); END; /
結果:
Before invoking procedure p: j = 10 k = 1.5E+001 Inside procedure p: IN a = 4 IN b = 0 OUT c = NULL IN OUT d = 1.5E+001 After invoking procedure p: j = 10 k = 1.5E+001 PL/SQL procedure successfully completed.
例9-16 NULL以外のデフォルト値を持つレコード型のOUT仮パラメータ
CREATE OR REPLACE PACKAGE r_types AUTHID DEFINER IS TYPE r_type_1 IS RECORD (f VARCHAR2(5) := 'abcde'); TYPE r_type_2 IS RECORD (f VARCHAR2(5)); END; / CREATE OR REPLACE PROCEDURE p ( x OUT r_types.r_type_1, y OUT r_types.r_type_2, z OUT VARCHAR2) AUTHID CURRENT_USER IS BEGIN DBMS_OUTPUT.PUT_LINE('x.f is ' || NVL(x.f,'NULL')); DBMS_OUTPUT.PUT_LINE('y.f is ' || NVL(y.f,'NULL')); DBMS_OUTPUT.PUT_LINE('z is ' || NVL(z,'NULL')); END; / DECLARE r1 r_types.r_type_1; r2 r_types.r_type_2; s VARCHAR2(5) := 'fghij'; BEGIN p (r1, r2, s); END; /
結果:
x.f is abcde y.f is NULL z is NULL PL/SQL procedure successfully completed.
サブプログラムのパラメータのエイリアシング
エイリアシングとは、同じメモリー位置に対して2つの異なる名前が存在することです。格納されている項目が複数のパスで表示可能な場合は、1つのパスでその項目を変更してからすべてのパスで変更を確認できます。
サブプログラムのパラメータのエイリアシングは、コンパイラが参照によって実パラメータを渡す場合に常に発生し、サブプログラムにカーソル変数パラメータが含まれる場合にも発生する可能性があります。
ここでのトピック
参照渡しパラメータによるサブプログラムのパラメータのエイリアシング
コンパイラが参照によって実パラメータを渡す場合、実パラメータと仮パラメータは同じメモリー位置を参照します。そのため、サブプログラムによって仮パラメータの値が変更されると、その変更は実パラメータに即座に反映されます。
コンパイラは常に参照によってIN
パラメータを渡しますが、サブプログラムはIN
パラメータに値を代入できないため、この場合のエイリアシングでは問題は発生しません。
コンパイラは、OUT
またはIN
OUT
パラメータにNOCOPY
が指定されると、そのパラメータを参照によって渡す可能性があります。NOCOPY
は単なるヒントで、コンパイラは、サブプログラムが起動されるたびに、NOCOPY
に従うか無視するかを暗黙的に決定します。したがって、エイリアシングの発生の有無は起動ごとに異なる可能性があるため、サブプログラムの結果は予測不能です。たとえば:
-
実パラメータがグローバル変数である場合、仮パラメータに対する代入は、グローバル・パラメータに反映される可能性があります(例9-17を参照)。
-
同じ変数が2つの仮パラメータの実パラメータである場合、いずれかの仮パラメータに対する代入は、両方の仮パラメータに即座に反映される可能性があります(例9-18を参照)。
-
実パラメータがパッケージ変数である場合、仮パラメータまたはパッケージ変数に対する代入は、仮パラメータとパッケージ変数の両方に即座に反映される可能性があります。
-
サブプログラムが未処理例外を戻して終了した場合、仮パラメータに対する代入は、実パラメータに反映される可能性があります。
関連項目:
コンパイラが常にNOCOPY
を無視する事例は、「NOCOPY」を参照してください
例9-17では、プロシージャにIN
OUT
NOCOPY
仮パラメータが含まれており、プロシージャはそのパラメータに値'aardvark'
を代入します。無名ブロックは、グローバル変数に値'aardwolf'
を代入し、そのグローバル変数をプロシージャに渡します。コンパイラがNOCOPY
ヒントに従う場合、グローバル変数の最終的な値は、'aardvark'
です。コンパイラがNOCOPY
ヒントを無視する場合、グローバル変数の最終的な値は、'aardwolf'
です。
例9-18では、プロシージャにIN
パラメータ、IN
OUT
パラメータおよびIN
OUT
NOCOPY
パラメータが含まれます。無名ブロックは、3つのすべての仮パラメータに同じ実パラメータ(グローバル変数)を使用してプロシージャを起動します。プロシージャは、IN
OUT
パラメータの値を変更してからIN
OUT
NOCOPY
パラメータの値を変更します。ただし、コンパイラがNOCOPY
ヒントに従う場合、後者の変更は、実パラメータに即座に反映されます。前者の変更は、プロシージャが正常に終了して制御が無名ブロックに戻された後に実パラメータに反映されます。
例9-17 実パラメータとしてのグローバル変数によるエイリアシング
DECLARE
TYPE Definition IS RECORD (
word VARCHAR2(20),
meaning VARCHAR2(200)
);
TYPE Dictionary IS VARRAY(2000) OF Definition;
lexicon Dictionary := Dictionary(); -- global variable
PROCEDURE add_entry (
word_list IN OUT NOCOPY Dictionary -- formal NOCOPY parameter
) IS
BEGIN
word_list(1).word := 'aardvark';
END;
BEGIN
lexicon.EXTEND;
lexicon(1).word := 'aardwolf';
add_entry(lexicon); -- global variable is actual parameter
DBMS_OUTPUT.PUT_LINE(lexicon(1).word);
END;
/
結果:
aardvark
例9-18 複数の仮パラメータに対する同じ実パラメータによるエイリアシング
DECLARE n NUMBER := 10; PROCEDURE p ( n1 IN NUMBER, n2 IN OUT NUMBER, n3 IN OUT NOCOPY NUMBER ) IS BEGIN n2 := 20; -- actual parameter is 20 only after procedure succeeds DBMS_OUTPUT.put_line(n1); -- actual parameter value is still 10 n3 := 30; -- might change actual parameter immediately DBMS_OUTPUT.put_line(n1); -- actual parameter value is either 10 or 30 END; BEGIN p(n, n, n); DBMS_OUTPUT.put_line(n); END; /
コンパイラがNOCOPY
ヒントに従う場合の結果:
10 30 20
コンパイラがNOCOPY
ヒントを無視する場合の結果:
10 10 30
カーソル変数パラメータによるサブプログラムのパラメータのエイリアシング
カーソル変数パラメータは、ポインタです。したがって、サブプログラムがあるカーソル変数パラメータを別のカーソル変数パラメータに代入すると、それらのパラメータは同じメモリー位置を参照します。このエイリアシングでは、意図しない結果が発生することがあります。
例9-19では、プロシージャに2つのカーソル変数パラメータ(emp_cv1
およびemp_cv2
)が含まれます。プロシージャは、emp_cv1
をオープンし、その値(ポインタ)をemp_cv2
に代入します。この場合、emp_cv1
とemp_cv2
は、同じメモリー位置を参照します。プロシージャがemp_cv1
をクローズすると、emp_cv2
もクローズされます。そのため、プロシージャがemp_cv2
からフェッチしようとすると、PL/SQLによって例外が呼び出されます。
例9-19 カーソル変数サブプログラム・パラメータによるエイリアシング
DECLARE TYPE EmpCurTyp IS REF CURSOR; c1 EmpCurTyp; c2 EmpCurTyp; PROCEDURE get_emp_data ( emp_cv1 IN OUT EmpCurTyp, emp_cv2 IN OUT EmpCurTyp ) IS emp_rec employees%ROWTYPE; BEGIN OPEN emp_cv1 FOR SELECT * FROM employees; emp_cv2 := emp_cv1; -- now both variables refer to same location FETCH emp_cv1 INTO emp_rec; -- fetches first row of employees FETCH emp_cv1 INTO emp_rec; -- fetches second row of employees FETCH emp_cv2 INTO emp_rec; -- fetches third row of employees CLOSE emp_cv1; -- closes both variables FETCH emp_cv2 INTO emp_rec; -- causes error when get_emp_data is invoked END; BEGIN get_emp_data(c1, c2); END; /
結果:
DECLARE * ERROR at line 1: ORA-01001: cursor number is invalid or does not exist ORA-06512: at line 19 ORA-06512: at line 22
サブプログラムのINパラメータのデフォルト値
IN
仮パラメータを宣言する場合、そのパラメータにデフォルト値を指定できます。デフォルト値を持つ仮パラメータは、その対応する実パラメータがサブプログラムの起動においてオプションであるため、オプション・パラメータと呼ばれます。実パラメータが省略されると、起動によって仮パラメータにデフォルト値が代入されます。デフォルト値を持たない仮パラメータは、その対応する実パラメータがサブプログラムの起動において必須であるため、必須パラメータと呼ばれます。
実パラメータを省略しても、対応する仮パラメータの値はNULL
になりません。仮パラメータの値をNULL
にするには、デフォルト値または実パラメータとしてNULL
を指定します。
例9-20では、プロシージャに1つの必須パラメータと2つのオプション・パラメータが含まれます。
例9-20では、プロシージャの起動によって、実パラメータが、それと対応する仮パラメータの宣言と同じ順序で指定されます(起動では位置表記法が使用されます)。位置表記法では、raise_salary
の2番目のパラメータを省略せずに、3番目のパラメータを指定します(途中のパラメータを省略するには、名前表記法または混合表記法を使用する必要があります)。詳細は、実パラメータの位置表記法、名前表記法および混合表記法を参照してください。
仮パラメータのデフォルト値には、パラメータに値を代入できる任意の式を指定できます(値およびパラメータのデータ型には互換性が必要です)。サブプログラムの起動で仮パラメータに実パラメータが指定されると、その起動ではデフォルト値が評価されません。
例9-21では、プロシージャp
に、ファンクションf
を起動するデフォルト値を持つパラメータが含まれます。ファンクションf
は、グローバル変数の値を増分します。p
が実パラメータなしで起動されると、p
はf
を起動し、f
はグローバル変数を増分します。p
が実パラメータ付きで起動されると、p
はf
を起動せず、グローバル変数の値は変化しません。
例9-22では、2つの必須パラメータが含まれるプロシージャを作成してそれを起動し、その後、3番目のオプション・パラメータを追加しています。3番目のパラメータはオプションであるため、元の起動も引き続き有効です。
例9-20 デフォルトのパラメータ値を使用したプロシージャ
DECLARE PROCEDURE raise_salary ( emp_id IN employees.employee_id%TYPE, amount IN employees.salary%TYPE := 100, extra IN employees.salary%TYPE := 50 ) IS BEGIN UPDATE employees SET salary = salary + amount + extra WHERE employee_id = emp_id; END raise_salary; BEGIN raise_salary(120); -- same as raise_salary(120, 100, 50) raise_salary(121, 200); -- same as raise_salary(121, 200, 50) END; /
例9-21 ファンクションによるデフォルトのパラメータ値の提供
DECLARE global PLS_INTEGER := 0; FUNCTION f RETURN PLS_INTEGER IS BEGIN DBMS_OUTPUT.PUT_LINE('Inside f.'); global := global + 1; RETURN global * 2; END f; PROCEDURE p ( x IN PLS_INTEGER := f() ) IS BEGIN DBMS_OUTPUT.PUT_LINE ( 'Inside p. ' || ' global = ' || global || ', x = ' || x || '.' ); DBMS_OUTPUT.PUT_LINE('--------------------------------'); END p; PROCEDURE pre_p IS BEGIN DBMS_OUTPUT.PUT_LINE ( 'Before invoking p, global = ' || global || '.' ); DBMS_OUTPUT.PUT_LINE('Invoking p.'); END pre_p; BEGIN pre_p; p(); -- default expression is evaluated pre_p; p(100); -- default expression is not evaluated pre_p; p(); -- default expression is evaluated END; /
結果:
Before invoking p, global = 0. Invoking p. Inside f. Inside p. global = 1, x = 2. -------------------------------- Before invoking p, global = 1. Invoking p. Inside p. global = 1, x = 100. -------------------------------- Before invoking p, global = 1. Invoking p. Inside f. Inside p. global = 2, x = 4. --------------------------------
例9-22 既存の起動を変更しないサブプログラム・パラメータの追加
プロシージャの作成:
CREATE OR REPLACE PROCEDURE print_name ( first VARCHAR2, last VARCHAR2 ) AUTHID DEFINER IS BEGIN DBMS_OUTPUT.PUT_LINE(first || ' ' || last); END print_name; /
プロシージャの起動:
BEGIN print_name('John', 'Doe'); END; /
結果:
John Doe
デフォルト値を持つ3番目のパラメータの追加:
CREATE OR REPLACE PROCEDURE print_name (
first VARCHAR2,
last VARCHAR2,
mi VARCHAR2 := NULL
) AUTHID DEFINER IS
BEGIN
IF mi IS NULL THEN
DBMS_OUTPUT.PUT_LINE(first || ' ' || last);
ELSE
DBMS_OUTPUT.PUT_LINE(first || ' ' || mi || '. ' || last);
END IF;
END print_name;
/
プロシージャの起動:
BEGIN print_name('John', 'Doe'); -- original invocation print_name('John', 'Public', 'Q'); -- new invocation END; /
結果:
John Doe John Q. Public
実パラメータの位置表記法、名前表記法および混合表記法
サブプログラムの起動時に、位置表記法、名前表記法または混合表記法のいずれかを使用して実パラメータを指定できます。表9-3に、これらの表記法の概要および比較を示します。
表9-3 PL/SQLの実パラメータの表記法
表記法 | 構文 | オプションのパラメータ | メリット | デメリット |
---|---|---|---|---|
位置 |
仮パラメータが宣言されている順序と同じ順序で実パラメータを指定します。 |
後続のオプション・パラメータを省略できます。 |
実パラメータを間違った順序で指定すると(特に実パラメータがリテラルの場合)、検出が困難な問題が発生する可能性があります。 仮パラメータのリストが変更される場合、そのリストで新しい後続のオプション・パラメータのみが取得されるのでなければ、サブプログラムの起動を変更する必要があります(例9-22を参照)。 コードの明瞭性とメンテナンス性が下がります。サブプログラムに多数のパラメータがある場合はお薦めしません。 |
|
名前 |
次の構文を使用して、任意の順序で実パラメータを指定します。 formal => actual
|
任意のオプション・パラメータを省略できます。 |
実パラメータを指定する場合の間違った順序はありません。 仮パラメータのリストで新しい必須パラメータが取得される場合にのみ、サブプログラムの起動を変更する必要があります。 自分以外のユーザーによって定義または管理されているサブプログラムを起動する場合に推奨されます。 |
|
混合 |
まず位置表記法を使用し、その後、残りのパラメータに名前表記法を使用します。 |
位置表記法では、後続のオプション・パラメータを省略できます。名前表記法では、任意のオプション・パラメータを省略できます。 |
必須パラメータの後にオプション・パラメータが続くサブプログラムを起動し、オプション・パラメータを数個のみ指定する必要がある場合に便利です。 |
位置表記法では、間違った順序で指定すると(特に実パラメータがリテラルの場合)、検出が困難な問題が発生する可能性があります。 仮パラメータのリストが変更される場合、位置表記法の変更が必要になる可能性があります。 |
例9-23では、プロシージャの起動で異なる表記法を使用していますが、同じ意味です。
例9-24では、異なる表記法による同等の起動を使用して、SQLのSELECT
文でPL/SQLファンクションcompute_bonus
を起動しています。
例9-23 無名ブロックでの異なる表記法を使用した同等な起動
DECLARE emp_num NUMBER(6) := 120; bonus NUMBER(6) := 50; PROCEDURE raise_salary ( emp_id NUMBER, amount NUMBER ) IS BEGIN UPDATE employees SET salary = salary + amount WHERE employee_id = emp_id; END raise_salary; BEGIN -- Equivalent invocations: raise_salary(emp_num, bonus); -- positional notation raise_salary(amount => bonus, emp_id => emp_num); -- named notation raise_salary(emp_id => emp_num, amount => bonus); -- named notation raise_salary(emp_num, amount => bonus); -- mixed notation END; /
例9-24 SELECT文での異なる表記法を使用した同等な起動
CREATE OR REPLACE FUNCTION compute_bonus ( emp_id NUMBER, bonus NUMBER ) RETURN NUMBER AUTHID DEFINER IS emp_sal NUMBER; BEGIN SELECT salary INTO emp_sal FROM employees WHERE employee_id = emp_id; RETURN emp_sal + bonus; END compute_bonus; / SELECT compute_bonus(120, 50) FROM DUAL; -- positional SELECT compute_bonus(bonus => 50, emp_id => 120) FROM DUAL; -- named SELECT compute_bonus(120, bonus => 50) FROM DUAL; -- mixed