ヘッダーをスキップ
Oracle Database PL/SQL言語リファレンス
11g リリース1(11.1)
E05670-03
  目次
目次
索引
索引

戻る
戻る
 
次へ
次へ
 

B PL/SQLでの識別子名の解決

この付録では、潜在的に意味の曖昧なプロシージャ文およびSQL文で、名前への参照をPL/SQLがどのように解決するかについて説明します。

ここでのトピック:

名前解決

コンパイルの際に、PL/SQLコンパイラは、PL/SQLサブプログラムの各名前と関連付けるオブジェクトを判別します。 名前は、ローカル変数、表、パッケージ、サブプログラム、スキーマなどを参照する場合があります。 オブジェクトが作成または削除されている場合、サブプログラムが再コンパイルされると、その関連付けが変更されることがあります。

内部有効範囲における宣言または定義で、外部有効範囲における別の宣言または定義が隠される可能性があります。 PL/SQLの名前では大/小文字が区別されないため、例B-1では、client変数の宣言によってClientデータ型の定義が隠されています。

例B-1 グローバル変数名およびローカル変数名の解決

BEGIN
  <<block1>>
  DECLARE
    TYPE Client IS RECORD (
      first_name VARCHAR2(20), last_name VARCHAR2(25));
      TYPE Customer IS RECORD (
         first_name VARCHAR2(20), last_name VARCHAR2(25));
  BEGIN
    DECLARE
      client Customer;
      -- hides definition of type Client in outer scope
      -- lead1  Client;
      -- not allowed; Client resolves to the variable client
      lead2  block1.Client;
      -- OK; refers to type Client
    BEGIN
      -- no processing, just an example of name resolution
      NULL;
    END;
  END;
END;
/

ブロック・ラベルblock1で参照を修飾すると、Clientデータ型を参照できます。

次のCREATE TYPE文では、2つ目の文によって警告が生成されます。 managerという名前の属性を作成すると、managerという名前の型が隠されるため、2つ目の属性の宣言は適切に実行されません。

CREATE TYPE manager AS OBJECT (dept NUMBER);
/
CREATE TYPE person AS OBJECT (manager NUMBER, mgr manager)
    -- raises a warning;
/

修飾名およびドット表記法の例

名前解決の際、コンパイラは、単なる未修飾の名前、ドットで区切られて連鎖した識別子、コレクションの索引付きのコンポーネントなど、様々な種類の参照に遭遇する可能性があります。 例B-2を参照してください。

例B-2 ドット表記法を使用した名前の修飾

CREATE OR REPLACE PACKAGE pkg1 AS
   m NUMBER;
   TYPE t1 IS RECORD (a NUMBER);
   v1 t1;
   TYPE t2 IS TABLE OF t1 INDEX BY PLS_INTEGER;
   v2 t2;
   FUNCTION f1 (p1 NUMBER) RETURN t1;
   FUNCTION f2 (q1 NUMBER) RETURN t2;
END pkg1;
/

CREATE OR REPLACE PACKAGE BODY pkg1 AS
   FUNCTION f1 (p1 NUMBER) RETURN t1 IS
     n NUMBER;
   BEGIN
-- (1) unqualified name
     n := m;
-- (2) dot-separated chain of identifiers
--     (package name used as scope qualifier
--     followed by variable name)
     n := pkg1.m;
-- (3) dot-separated chain of identifiers
--     (package name used as scope
--     qualifier followed by function name
--     also used as scope qualifier
--     followed by parameter name)
     n := pkg1.f1.p1;
-- (4) dot-separated chain of identifiers
--     (variable name followed by
--     component selector)
     n := v1.a;
-- (5) dot-separated chain of identifiers
--    (package name used as scope
--    qualifier followed by variable name
--    followed by component selector)
     n := pkg1.v1.a;
-- (6) indexed name followed by component selector
     n := v2(10).a;
-- (7) function call followed by component selector
     n := f1(10).a;
-- (8) function call followed by indexing followed by
--     component selector
     n := f2(10)(10).a;
-- (9) function call (which is a dot-separated
--     chain of identifiers, including schema name used
-- as scope qualifier followed by package name used
-- as scope qualifier followed by function name)
-- followed by component selector of the returned
-- result followed by indexing followed by component selector
     n := hr.pkg1.f2(10)(10).a;
-- (10) variable name followed by component selector
     v1.a := p1;
     RETURN v1;
   END f1;

   FUNCTION f2 (q1 NUMBER) RETURN t2 IS
   v_t1 t1;
   v_t2 t2;
   BEGIN
     v_t1.a := q1;
     v_t2(1) := v_t1;
     RETURN v_t2;
   END f2;
END pkg1;
/

ファンクション本体で宣言されているプライベート変数への外部参照は、無効です。 たとえば、ファンクションf2hr.pkg1.f1.nなど、ファンクションf1で宣言されている変数nへの外部参照によって例外が発生します。 「PL/SQLパッケージのプライベート項目とパブリック項目」を参照してください。

ドット表記法は、レコード・フィールド、オブジェクト属性、およびパッケージや他のスキーマ内の項目を識別するために使用します。 これらの項目を組み合せる際、複数レベルのドットを含む式を使用する必要がある場合がありますが、その場合は各ドットが示すものがわかりにくくなることもあります。 組合せの例を次に示します。

PL/SQLとSQLでの名前解決の相違点

PL/SQLとSQLの名前解決ルールはよく似ています。 取得回避規則に従っている場合は、いくつかの違いは回避できます。 互換性のため、SQLルールはPL/SQLと比較して、より許容性が高くなっています。 そのほとんどが状況依存なSQLルールでは、PL/SQLルールで認識されるよりも多くの状況とDML文が、有効なものと認識されます。

SQLネーミング規則の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

取得

別の有効範囲における宣言または型の定義が参照の正常な解決の妨げになる場合、その宣言または定義が参照を「取得する」と呼びます。 通常、取得は移行またはスキーマのアップグレードの結果として行われます。 取得には、内部、同一有効範囲および外部の3種類があります。 内部および同一有効範囲の取得はSQLスコープにのみ適用されます。

ここでのトピック:

内部取得

内部取得が発生するのは、内部有効範囲に含まれる名前が外部有効範囲のエンティティを参照しておらず、次のような状態が発生した場合です。

  • 内部有効範囲に含まれるエンティティに名前が解決される場合。

  • 識別子の一部が内部有効範囲に取得され、参照を完全に解決することができず、プログラムでエラーが発生する場合。

参照が別の有効な名前を指す場合、プログラムが目的と異なる動作を行う理由を認識できない可能性があります。

次の例では、内側のSELECT文におけるcol2への参照は、表tab2col2という名前の列を持たないため、表tab1の列col2にバインドされます。

CREATE TABLE tab1 (col1 NUMBER, col2 NUMBER);
INSERT INTO tab1 VALUES (100, 10);
CREATE TABLE tab2 (col1 NUMBER);
INSERT INTO tab2 VALUES (100);

CREATE OR REPLACE PROCEDURE proc AS
   CURSOR c1 IS SELECT * FROM tab1
      WHERE EXISTS (SELECT * FROM tab2 WHERE col2 = 10);
BEGIN
   NULL;
END;
/

この例で、次に示すように、表tab2に列col2を追加した場合を考えます。

ALTER TABLE tab2 ADD (col2 NUMBER);

この場合には、プロシージャprocは無効となり、次に使用する際に、自動的に再コンパイルされます。 ただし、再コンパイルの際に、tab2は内部有効範囲にあるため、内側のSELECT文のcol2tab2の列col2にバインドされます。 したがって、表tab2への列col2の追加によって、col2への参照は取得されます。

コレクションやオブジェクト型を使用することによって、さらに多くの内部取得が発生する可能性があります。 次の例では、hr.tab2.aへの参照は、問合せの外部有効範囲で参照することのできる表の別名hrを経由して、表tab1の列tab2の属性aに解決されます。

CREATE TYPE type1 AS OBJECT (a NUMBER);
/
CREATE TABLE tab1 (tab2 type1);
INSERT INTO tab1 VALUES ( type1(10) );
CREATE TABLE tab2 (x NUMBER);
INSERT INTO tab2 VALUES ( 10 );

-- in the following,
-- alias tab1 with same name as schema name,
-- which is not a good practice
-- but is used here for illustration purpose
-- note lack of alias in second SELECT
SELECT * FROM tab1 hr
   WHERE EXISTS (SELECT * FROM hr.tab2 WHERE x = hr.tab2.a);

この例で、内側の副問合せに現れる表hr.tab2に列名aを追加することを考えます。 問合せが処理されると、hr.tab2.aへの参照がスキーマhr内の表tab2の列aに解決されるため、内部取得が発生します。 内部取得を防止するには、「DML文の内部取得の回避」で説明するルールに従います。 これらのルールに従って、この問合せを次のように書き換えます。

SELECT * FROM hr.tab1 p1
  WHERE EXISTS (SELECT * FROM hr.tab2 p2 WHERE p2.x = p1.tab2.a);

同一有効範囲の取得

SQLスコープで、同一有効範囲の取得が発生するのは、結合に使用される2つの表のどちらかに列が追加され、どちらの表にも同じ列名が存在する場合です。 結合問合せのその列名を事前に参照できます。 エラーを回避するには、列名を表名で修飾する必要があります。

外部取得

外部取得が発生するのは、過去に内部有効範囲内のエンティティに解決されていた内部有効範囲内の名前が、外部有効範囲に解決された場合です。 SQLとPL/SQLは、外部取得を防止する設計になっています。 この状態を回避するためのアクションは必要ありません。

DML文の内部取得の回避

次のルールを遵守することによって、DML文における内部取得を防止できます。

文がユーザー定義のオブジェクト型の列を持つ表を参照している場合には、schema_name.table_nameで参照を修飾しても内部取得は防止できません。

ユーザー定義のオブジェクト型の列によって、より多くの内部取得が発生する可能性があります。 問題を最小限に抑えるため、表の別名の使用に関する次のルールが名前解決アルゴリズムに含まれています。

ここでのトピック:

属性およびメソッドへの参照の修飾

属性およびメソッドへのすべての参照が、表の別名によって修飾されている必要があります。 表を参照する場合、その表に格納されているオブジェクトの属性やメソッドを参照するときには、表名に別名を添付する必要があります。 次の例に示すとおり、属性またはメソッドへの列修飾された参照は、その参照に接頭辞として表名が使用されている場合は使用できません。

CREATE TYPE t1 AS OBJECT (x NUMBER);
/
CREATE TABLE tb1 (col1 t1);

BEGIN
-- following inserts are allowed without an alias
-- because there is no column list
  INSERT INTO tb1 VALUES ( t1(10) );
  INSERT INTO tb1 VALUES ( t1(20) );
  INSERT INTO tb1 VALUES ( t1(30) );
END;
/
BEGIN
  UPDATE tb1 SET col1.x = 10
  WHERE col1.x = 20; -- error, not allowed
END;
/
BEGIN
  UPDATE tb1 SET tb1.col1.x = 10
  WHERE tb1.col1.x = 20; -- not allowed
END;
/
BEGIN
  UPDATE hr.tb1 SET hr.tb1.col1.x = 10
  WHERE hr.tb1.col1.x = 20; -- not allowed
END;
/
BEGIN -- following allowed with table alias
  UPDATE hr.tb1 t set t.col1.x = 10
  WHERE t.col1.x = 20;
END;
/
DECLARE
  y NUMBER;
BEGIN -- following allowed with table alias
  SELECT t.col1.x INTO y FROM tb1 t
  WHERE t.col1.x = 30;
END;
/
BEGIN
  DELETE FROM tb1
  WHERE tb1.col1.x = 10; -- not allowed
END;
/
BEGIN -- following allowed with table alias
  DELETE FROM tb1 t
  WHERE t.col1.x = 10;
END;
/

行の式への参照の修飾

行の式は、表の別名への参照として解決する必要があります。 行の式をREFおよびVALUEに渡したり、UPDATE文のSET句に行の式を使用できます。 次に例を示します。

CREATE TYPE t1 AS OBJECT (x number);
/
CREATE TABLE ot1 OF t1;

BEGIN
-- following inserts are allowed without an alias
-- because there is no column list
  INSERT INTO ot1 VALUES ( t1(10) );
  INSERT INTO ot1 VALUES ( 20 );
  INSERT INTO ot1 VALUES ( 30 );
END;
/
BEGIN
  UPDATE ot1 SET VALUE(ot1.x) = t1(20)
  WHERE VALUE(ot1.x) = t1(10); -- not allowed
END;
/
BEGIN -- following allowed with table alias
  UPDATE ot1 o SET o = (t1(20)) WHERE o.x = 10;
END;
/
DECLARE
  n_ref REF t1;
BEGIN -- following allowed with table alias
  SELECT REF(o) INTO n_ref FROM ot1 o
  WHERE VALUE(o) = t1(30);
END;
/
DECLARE
  n t1;
BEGIN -- following allowed with table alias
  SELECT VALUE(o) INTO n FROM ot1 o
  WHERE VALUE(o) = t1(30);
END;
/
DECLARE
  n NUMBER;
BEGIN -- following allowed with table alias
  SELECT o.x INTO n FROM ot1 o WHERE o.x = 30;
END;
/
BEGIN
  DELETE FROM ot1
  WHERE VALUE(ot1) = (t1(10)); -- not allowed
END;
/
BEGIN -- folowing allowed with table alias
  DELETE FROM ot1 o
  WHERE VALUE(o) = (t1(20));
END;
/