識別子の有効範囲と可視性
識別子の有効範囲とは、その識別子の参照が可能な、PL/SQLユニットの領域です。識別子の可視性とは、修飾なしでその識別子の参照が可能な、PL/SQLユニットの領域です。識別子は、自身が宣言されているPL/SQLユニットに対してローカルです。そのユニットにサブユニットがある場合、識別子はサブユニットに対してグローバルです。
サブユニットでグローバル識別子が再宣言されると、そのサブユニット内では両方の識別子が有効範囲内にあることになりますが、ローカル識別子のみが表示されます。サブユニットでグローバル識別子を参照するには、グローバル識別子が宣言されているユニットの名前でグローバル識別子を修飾する必要があります。そのユニットに名前がない場合、サブユニットでグローバル識別子を参照することはできません。
PL/SQLユニットは、あるユニットから同じレベルの他のユニットで宣言されている識別子への参照はできません。そのような識別子は、そのブロックに対してローカルでもグローバルでもないためです。
同じPL/SQLユニット内で、同じ識別子を2回宣言することはできません。そうすると、重複した識別子を参照するときにエラーが発生します。
2つの異なるユニットであれば、同じ識別子を宣言できます。その識別子で表される2つのオブジェクトは区別されます。片方を変更しても、もう片方には影響しません。
同じ有効範囲内では、混乱や予期しない結果を回避するためにラベルとサブプログラムに一意の名前を付けます。
例
例3-17 識別子の有効範囲と可視性
この例では、いくつかの識別子の有効範囲と可視性を示します。最初のサブブロックで、グローバル識別子a
が再宣言されています。グローバル変数a
を参照するには、最初のサブブロックで、外側のブロックの名前を使用して変数を修飾する必要がありますが、外側のブロックには名前がありません。そのため、最初のサブブロックではグローバル変数a
を参照することはできず、参照できるのはローカル変数a
のみです。サブブロックは同じレベルにあるため、最初のサブブロックはd
を参照できず、2番目のサブブロックはc
を参照できません。
-- Outer block: DECLARE a CHAR; -- Scope of a (CHAR) begins b REAL; -- Scope of b begins BEGIN -- Visible: a (CHAR), b -- First sub-block: DECLARE a INTEGER; -- Scope of a (INTEGER) begins c REAL; -- Scope of c begins BEGIN -- Visible: a (INTEGER), b, c NULL; END; -- Scopes of a (INTEGER) and c end -- Second sub-block: DECLARE d REAL; -- Scope of d begins BEGIN -- Visible: a (CHAR), b, d NULL; END; -- Scope of d ends -- Visible: a (CHAR), b END; -- Scopes of a (CHAR) and b end /
例3-18 ブロック・ラベルによる、再宣言されたグローバル識別子の修飾
この例では、外側のブロックにouter
という名前のラベルを付けています。そのため、サブブロックでは、グローバル変数birthdate
を再宣言した後、ブロックのラベルを使用して変数名を修飾することで、このグローバル変数を参照できます。サブブロックでは、単純名によってローカル変数birthdate
も参照できます。
<<outer>> -- label DECLARE birthdate DATE := TO_DATE('09-AUG-70', 'DD-MON-YY'); BEGIN DECLARE birthdate DATE := TO_DATE('29-SEP-70', 'DD-MON-YY'); BEGIN IF birthdate = outer.birthdate THEN DBMS_OUTPUT.PUT_LINE ('Same Birthday'); ELSE DBMS_OUTPUT.PUT_LINE ('Different Birthday'); END IF; END; END; /
結果:
Different Birthday
例3-19 サブプログラム名による識別子の修飾
この例では、プロシージャcheck_credit
で、変数rating
とファンクションcheck_rating
を宣言しています。ファンクションで変数を再宣言します。その後、ファンクションはグローバル変数をプロシージャ名で修飾して参照します。
CREATE OR REPLACE PROCEDURE check_credit (credit_limit NUMBER) AS rating NUMBER := 3; FUNCTION check_rating RETURN BOOLEAN IS rating NUMBER := 1; over_limit BOOLEAN; BEGIN IF check_credit.rating <= credit_limit THEN -- reference global variable over_limit := FALSE; ELSE over_limit := TRUE; rating := credit_limit; -- reference local variable END IF; RETURN over_limit; END check_rating; BEGIN IF check_rating THEN DBMS_OUTPUT.PUT_LINE ('Credit rating over limit (' || TO_CHAR(credit_limit) || '). ' || 'Rating: ' || TO_CHAR(rating)); ELSE DBMS_OUTPUT.PUT_LINE ('Credit rating OK. ' || 'Rating: ' || TO_CHAR(rating)); END IF; END; / BEGIN check_credit(1); END; /
結果:
Credit rating over limit (1). Rating: 3
例3-20 同じ有効範囲での重複する識別子
同じPL/SQLユニット内で、同じ識別子を2回宣言することはできません。この例に示すとおり、これを行うと、重複する識別子を参照したときにエラーが発生します。
DECLARE
id BOOLEAN;
id VARCHAR2(5); -- duplicate identifier
BEGIN
id := FALSE;
END;
/
結果:
id := FALSE; * ERROR at line 5: ORA-06550: line 5, column 3: PLS-00371: at most one declaration for 'ID' is permitted ORA-06550: line 5, column 3: PL/SQL: Statement ignored
例3-21 異なるユニットでの同じ識別子の宣言
2つの異なるユニットであれば、同じ識別子を宣言できます。その識別子で表される2つのオブジェクトは区別されます。この例に示すとおり、1つを変更しても、もう1つに影響はありません。同じ有効範囲内では、混乱や予期しない結果を回避するためにラベルとサブプログラムに一意の名前を付けます。
DECLARE PROCEDURE p IS x VARCHAR2(1); BEGIN x := 'a'; -- Assign the value 'a' to x DBMS_OUTPUT.PUT_LINE('In procedure p, x = ' || x); END; PROCEDURE q IS x VARCHAR2(1); BEGIN x := 'b'; -- Assign the value 'b' to x DBMS_OUTPUT.PUT_LINE('In procedure q, x = ' || x); END; BEGIN p; q; END; /
結果:
In procedure p, x = a In procedure q, x = b
例3-22 同じ有効範囲内で同じ名前を持つラベルとサブプログラム
この例では、echo
がブロックとサブプログラムの両方の名前になっています。ブロックおよびサブプログラムの両方で、x
という変数を宣言しています。サブプログラム内では、echo
.x
は、グローバル変数x
ではなく、ローカル変数x
を参照します。
<<echo>> DECLARE x NUMBER := 5; PROCEDURE echo AS x NUMBER := 0; BEGIN DBMS_OUTPUT.PUT_LINE('x = ' || x); DBMS_OUTPUT.PUT_LINE('echo.x = ' || echo.x); END; BEGIN echo; END; /
結果:
x = 0 echo.x = 0
例3-23 複数の重複したラベルを使用するブロック
この例では、外側のブロックに対して2つのラベルcompute_ratio
とanother_label
が存在します。2番目のラベルは、内側のブロックに再度現れます。内側のブロック内では、another_label
.denominator
はローカル変数denominator
を参照し、グローバル変数denominator
を参照しないため、ZERO_DIVIDE
エラーが発生します。
<<compute_ratio>> <<another_label>> DECLARE numerator NUMBER := 22; denominator NUMBER := 7; BEGIN <<another_label>> DECLARE denominator NUMBER := 0; BEGIN DBMS_OUTPUT.PUT_LINE('Ratio with compute_ratio.denominator = '); DBMS_OUTPUT.PUT_LINE(numerator/compute_ratio.denominator); DBMS_OUTPUT.PUT_LINE('Ratio with another_label.denominator = '); DBMS_OUTPUT.PUT_LINE(numerator/another_label.denominator); EXCEPTION WHEN ZERO_DIVIDE THEN DBMS_OUTPUT.PUT_LINE('Divide-by-zero error: can''t divide ' || numerator || ' by ' || denominator); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('Unexpected error.'); END another_label; END compute_ratio; /
結果:
Ratio with compute_ratio.denominator = 3.14285714285714285714285714285714285714 Ratio with another_label.denominator = Divide-by-zero error: cannot divide 22 by 0