ヘッダーをスキップ
Oracle® Database PL/SQL言語リファレンス
11gリリース2 (11.2)
B56260-09
  目次へ移動
目次
索引へ移動
索引

前
 
次
 

B PL/SQLの名前解決

この付録では、PL/SQLの名前解決(識別子に対するあいまいな参照を、PL/SQLコンパイラがどのように解決するか)について説明します。

コンパイル単位内で識別子を変更(識別子の追加、名前変更または削除)すると、あいまいでない識別子参照があいまいになる可能性があります。


注意:

ストアドPL/SQLユニットのAUTHIDプロパティは、実行時にユニットによって発行されるSQL文の名前解決に影響します。詳細は、「実行者権限および定義者権限(AUTHIDプロパティ)」を参照してください。

ここでのトピック

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

ある名前付き項目が別の名前付き項目に属する場合は、ドット表記法を使用して「子」項目の名前を「親」項目の名前で修飾できます(場合よっては必須)。次に例を示します。

参照する項目 修飾に使用する必要がある項目 使用する構文
レコードのフィールド レコードの名前 record_name.field_name
コレクションのメソッド コレクションの名前 collection_name.method
疑似列CURRVAL 順序の名前 sequence_name.CURRVAL
疑似列NEXTVAL 順序の名前 sequence_name.NEXTVAL

識別子が名前付きPL/SQLユニットで宣言されている場合、次の構文を使用して単純名(宣言に含まれる名前)をユニット(ブロック、サブプログラムまたはパッケージ)の名前で修飾できます。

unit_name.simple_identifier_name

識別子を参照できない場合は、識別子の名前を修飾する必要があります(「識別子の有効範囲と可視性」を参照)。

識別子が別のスキーマに属する場合は、次の構文を使用して識別子の名前をスキーマの名前で修飾する必要があります。

schema_name.package_name

例B-1に示すとおり、単純名は複数の名前で修飾できます。

例B-1 修飾名

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
     n := m;             -- Unqualified variable name
     n := pkg1.m;        -- Variable name qualified by package name
     n := pkg1.f1.p1;    -- Parameter name qualified by function name,
                         --  which is qualified by package name
     n := v1.a;          -- Variable name followed by component name
     n := pkg1.v1.a;     -- Variable name qualified by package name
                         --  and followed by component name
     n := v2(10).a;      -- Indexed name followed by component name
     n := f1(10).a;      -- Function invocation followed by component name
     n := f2(10)(10).a;  -- Function invocation followed by indexed name
                         --  and followed by component name
     n := hr.pkg1.f2(10)(10).a;  -- Schema name, package name,
                                 -- function invocation, index, component name
     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;
/

あいまいになる可能性のある修飾名の例をいくつか次に示します。

  • ファンクションの戻り値のフィールドまたは属性の例:

    func_name().field_name
    func_name().attribute_name
    
  • 別のスキーマが所有するスキーマ・オブジェクトの例:

    schema_name.table_name
    schema_name.procedure_name()
    schema_name.type_name.member_name()
    
  • 別のユーザーが所有するパッケージ・オブジェクトの例:

    schema_name.package_name.procedure_name()
    schema_name.package_name.record_name.field_name
    
  • ADTを含むレコードの例:

    record_name.field_name.attribute_name
    record_name.field_name.member_name()
    

列名の優先順位

ローカル変数または仮パラメータのいずれかと列の両方に属する名前をSQL文で参照すると、列名が優先されます。


注意:

変数名またはパラメータ名が列名として解釈されると、データが誤って削除、変更または挿入されることがあります。

例B-2では、last_nameという名前がローカル変数と列の両方に属しています(名前は大/小文字が区別されません)。そのため、WHERE句に含まれるlast_nameの参照は両方とも列に解決され、すべての行が削除されます。

例B-2 列名に解釈されるために意図していない結果を招く変数名

DROP TABLE employees2;
CREATE TABLE employees2 AS
  SELECT LAST_NAME FROM employees;
 
DECLARE
  last_name  VARCHAR2(10) := 'King';
BEGIN
  DELETE FROM employees2 WHERE LAST_NAME = last_name;
  DBMS_OUTPUT.PUT_LINE('Deleted ' || SQL%ROWCOUNT || ' rows.');
END;
/
 

結果:

Deleted 107 rows.

例B-3では、変数に異なる名前を付けることで、例B-2の問題を解決しています。

例B-3 異なる変数名の使用による例B-2の修正

DECLARE
  v_last_name  VARCHAR2(10) := 'King';
BEGIN
  DELETE FROM employees2 WHERE LAST_NAME = v_last_name;
  DBMS_OUTPUT.PUT_LINE('Deleted ' || SQL%ROWCOUNT || ' rows.');
END;
/
 

結果:

Deleted 2 rows.

例B-4では、ブロックにラベルを付け、変数名をブロック名で修飾することで、例B-2の問題を解決しています。

例B-4 ブロック・ラベルの使用による例B-2の修正

<<main>>
DECLARE
  last_name  VARCHAR2(10) := 'King';
BEGIN
  DELETE FROM employees2 WHERE last_name = main.last_name;
  DBMS_OUTPUT.PUT_LINE('Deleted ' || SQL%ROWCOUNT || ' rows.');
END;
/
 

結果:

Deleted 2 rows.

例B-5のファンクションdept_nameには、表DEPARTMENTSの列名が名前になっている仮パラメータとローカル変数があります。パラメータおよび変数の名前をファンクション名で修飾して、列名と区別しています。

例B-5 名前解決でのサブプログラム名

DECLARE
  FUNCTION dept_name (department_id IN NUMBER)
    RETURN departments.department_name%TYPE
  IS
    department_name  departments.department_name%TYPE;
  BEGIN
    SELECT department_name INTO dept_name.department_name
      --   ^column              ^local variable
    FROM departments
    WHERE department_id = dept_name.department_id;
    --    ^column         ^formal parameter
    RETURN department_name;
  END dept_name;
BEGIN
  FOR item IN (
    SELECT department_id
    FROM departments
    ORDER BY department_name) LOOP
 
      DBMS_OUTPUT.PUT_LINE ('Department: ' || dept_name(item.department_id));
  END LOOP;
END;
/
 

結果:

Department: Accounting
Department: Administration
Department: Benefits
Department: Construction
Department: Contracting
Department: Control And Credit
Department: Corporate Tax
Department: Executive
Department: Finance
Department: Government Sales
Department: Human Resources
Department: IT
Department: IT Helpdesk
Department: IT Support
Department: Manufacturing
Department: Marketing
Department: NOC
Department: Operations
Department: Payroll
Department: Public Relations
Department: Purchasing
Department: Recruiting
Department: Retail Sales
Department: Sales
Department: Shareholder Services
Department: Shipping
Department: Treasury

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

PL/SQLとSQLの名前解決ルールはよく似ています。ただし、次のことに注意してください。

  • PL/SQLルールはSQLルールと比較して、より許容性が低くなっています。

    ほとんどのSQLルールが状況依存であるため、PL/SQLルールで認識されるよりも多くの状況が有効なものと認識されます。

  • PL/SQLとSQLでは、修飾名の解決方法が異なります。

    たとえば、HR.JOBSという表名を解決する場合は次のとおりです。

    • PL/SQLでは、まず現行スキーマでHRという名前のパッケージ、型、表およびビューが検索され、次にパブリック・シノニムが検索され、最後にHRスキーマでJOBSという名前のオブジェクトが検索されます。

    • SQLでは、まずHRスキーマでJOBSという名前のオブジェクトが検索され、次に現行スキーマでHRという名前のパッケージ、型、表およびビューが検索されます。

PL/SQLとSQLの名前解決ルールのわずかな相違点に起因する問題を回避するには、「SELECT文およびDML文の内部取得の回避」の推奨事項に従います。


注意:

PL/SQLコンパイラで静的SQL文を処理する場合は、その文がSQLサブシステムに送信され、そこで文に含まれる名前がSQLルールを使用してSQLサブシステムによって解決されます。詳細は、「静的SQL文における名前解決」を参照してください。

静的SQL文における名前解決

静的SQLの詳細は、第6章「PL/SQLの静的SQL」を参照してください。

PL/SQLコンパイラが静的SQL文を見つけると、次の処理が行われます。

  1. その文がSELECT文の場合、PL/SQLコンパイラはINTO句を削除します。

  2. PL/SQLコンパイラは、その文をSQLサブシステムに送信します。

  3. SQLサブシステムで、その文の構文がチェックされます。

    構文が正しくない場合、PL/SQLユニットのコンパイルは失敗します。構文が正しい場合は、SQLサブシステムによって表の名前が決定され、そのSQL文の有効範囲でその他の名前の解決が試行されます。

  4. そのSQL文の有効範囲で名前を解決できない場合、SQLサブシステムは、その名前をPL/SQLコンパイラに送り返します。この名前は、エスケープ識別子と呼ばれます。

  5. PL/SQLコンパイラは、エスケープ識別子を解決しようとします。

    まず、コンパイラは、PL/SQLユニットの有効範囲で識別子を解決しようとします。それが失敗すると、コンパイラは、スキーマの有効範囲で識別子を解決しようとします。それが失敗すると、PL/SQLユニットのコンパイルは失敗します。

  6. PL/SQLユニットのコンパイルが成功すると、PL/SQLコンパイラは、静的SQL文と等価な通常のSQL文テキストを生成し、生成されたコンピュータ・コードとともにそのテキストを格納します。

  7. 実行時に、PL/SQLランタイム・システムによって、通常のSQL文を解析、バインド、実行するルーチンが起動されます。

    バインド変数はエスケープ識別子です(手順 4を参照)。

  8. この文がSELECT文の場合、PL/SQLランタイム・システムでは、手順 1でPL/SQLコンパイラによって削除されたINTO句に指定されたPL/SQLターゲットに結果が格納されます。

取得

宣言または定義が、別の有効範囲における参照の正常な解決の妨げになる場合、その宣言または定義が参照を「取得する」と呼びます。通常、取得は移行またはスキーマのアップグレードの結果として行われます。

ここでのトピック


注意:

同一有効範囲の取得および内部取得は、SQLスコープでのみ発生します。

外部取得

外部取得が発生するのは、内部有効範囲内の項目に解決されていた内部有効範囲内の名前が、外部有効範囲内の項目に解決される場合です。PL/SQLとSQLは両方とも、外部取得を防止する設計になっているので、外部取得の回避に慎重になる必要はありません。

同一有効範囲の取得

同一有効範囲の取得が発生するのは、結合に使用される2つの表のどちらかに列が追加され、この新しい列の名前が他の表の列名と同じである場合です。その名前の列が1つの表にのみ存在する場合は、名前が未修飾で結合に現れる可能性があります。同一有効範囲の取得を回避するには、その列名が結合に現れるすべての場所で、列名を適切な表名で修飾する必要があります。

内部取得

内部取得が発生するのは、外部有効範囲内の項目に解決されていた内部有効範囲内の名前が、内部有効範囲内の項目に解決される場合か解決できない場合のいずれかです。最初のケースでは結果が変わる可能性があります。2番目のケースではエラーが発生します。

例B-6では、新しい列は、同じ名前の古い列の参照を取得します。col2は、新しい列col2が表tab2に追加される前はtab1.col2に解決され、追加された後はtab2.col2に解決されます。

例B-6 列の参照の内部取得

tab1にはcol2という名前の列がありますが、表tab2にはありません。

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

DROP TABLE tab2;
CREATE TABLE tab2 (col1 NUMBER);
INSERT INTO tab2 (col1) VALUES (100);

そのため、内側のSELECT文では、col2への参照が列tab1.col2に解決されます。

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

tab2への列col2の追加:

ALTER TABLE tab2 ADD (col2 NUMBER);

これでプロシージャprocは無効になりました。次に起動する際に、このプロシージャはデータベースによって自動的に再コンパイルされ、内側のSELECT文におけるcol2への参照は、列tab2.col2に解決されます。

内部取得を防止するには、「SELECT文およびDML文の内部取得の回避」のルールに従います。

SELECT文およびDML文の内部取得の回避

次の推奨事項を遵守することによって、SELECT文、SELECT INTO文およびDML文における参照の内部取得を防止できます。

  • 文内の各表に対して一意の別名を指定します。

  • 文で参照される項目を所有しているスキーマと同じ名前を表の別名に指定しないようにします。

  • 文内の列の各参照を適切な表の別名で修飾します。

例B-7では、スキーマhrが表tab1およびtab2を所有しています。表tab1にはtab2という名前の列があり、その抽象データ型(ADT)は属性aを持っています。表tab2にはaという名前の列はありません。推奨事項に反して、この問合せでは、表tab1に別名hrを指定して表tab2を参照しています。そのため、この問合せ内の参照hr.tab2.aは、表tab1、列tab2、属性aに解決されます。その後、この例では、列aを表tab2に追加しています。今度は、問合せ内の参照hr.tab2.aは、スキーマhr、表tab2、列aに解決されます。表tab2の列aは、表tab1の列tab2の属性aへの参照を取得します。

例B-7 属性の参照の内部取得

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

DROP TABLE tab2;
CREATE TABLE tab2 (x NUMBER);
INSERT INTO tab2 (x) VALUES (10);

/* Alias tab1 with same name as schema name,
   a bad practice used here for illustration purpose.
   Note lack of alias in second SELECT statement. */

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

結果:

TAB2(A)
---------------
 
TYPE1(10)
 
1 row selected.

tab2(スキーマhrに属する)への列aの追加:

ALTER TABLE tab2 ADD (a NUMBER);

ここでこの問合せを実行すると、hr.tab2.aは、スキーマhr、表tab2、列aに解決されます。この内部取得を回避するために、問合せに推奨事項を適用します。

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

ここでのトピック:

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

表の要素の属性やメソッドを参照するには、表に別名を付け、その別名を使用して、属性やメソッドに対する参照を修飾します。

例B-8の表tbl1には、属性xを持つADTであるデータ型t1の列col1があります。この例は、tbl1.col1.xへの適切な参照と適切でない参照をいくつか示しています。

例B-8 ADT属性の参照の修飾

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

次のINSERT文の参照では、列リストがないため、別名は不要です。

BEGIN
  INSERT INTO tb1 VALUES ( t1(10) );
  INSERT INTO tb1 VALUES ( t1(20) );
  INSERT INTO tb1 VALUES ( t1(30) );
END;
/

属性xへの次の参照では、エラーORA-00904が発生します。

UPDATE tb1 SET col1.x = 10 WHERE col1.x = 20;

UPDATE tb1 SET tb1.col1.x = 10 WHERE tb1.col1.x = 20;

UPDATE hr.tb1 SET hr.tb1.col1.x = 10 WHERE hr.tb1.col1.x = 20;

DELETE FROM tb1 WHERE tb1.col1.x = 10;

属性xへの表の別名を使用した次の参照は、適切です。

UPDATE hr.tb1 t SET t.col1.x = 10 WHERE t.col1.x = 20;

DECLARE
  y NUMBER;
BEGIN
  SELECT t.col1.x INTO y FROM tb1 t WHERE t.col1.x = 30;
END;
/

DELETE FROM tb1 t WHERE t.col1.x = 10;

行の式への参照の修飾

行の式は、表の別名への参照として解決する必要があります。行の式は、UPDATE文のSET句で使用したり、SQLファンクションREFまたはVALUEのパラメータにすることができます。

例B-9の表ot1は、属性xを持つADTであるデータ型t1の要素のスタンドアロンのネストした表です。この例は、行の式への適切な参照と適切でない参照をいくつか示しています。

例B-9 行の式への参照の修飾

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

BEGIN
  INSERT INTO ot1 VALUES (t1(10));
  INSERT INTO ot1 VALUES (20);
  INSERT INTO ot1 VALUES (30);
END;
/

次の参照では、エラーORA-00904が発生します。

UPDATE ot1 SET VALUE(ot1.x) = t1(20) WHERE VALUE(ot1.x) = t1(10);

DELETE FROM ot1 WHERE VALUE(ot1) = (t1(10));

表の別名を使用した次の参照は、適切です。

UPDATE ot1 o SET o = (t1(20)) WHERE o.x = 10;

DECLARE
  n_ref  REF t1;
BEGIN
  SELECT REF(o) INTO n_ref FROM ot1 o WHERE VALUE(o) = t1(30);
END;
/

DECLARE
  n t1;
BEGIN
  SELECT VALUE(o) INTO n FROM ot1 o WHERE VALUE(o) = t1(30);
END;
/

DECLARE
  n NUMBER;
BEGIN
  SELECT o.x INTO n FROM ot1 o WHERE o.x = 30;
END;
/

DELETE FROM ot1 o WHERE VALUE(o) = (t1(20));