某些块(如子程序或匿名块)中声明的变量可以从其他块的可执行部分或异常部分访问,具体取决于它们的相对位置。
访问变量意味着能够在 SQL 语句或 SPL 语句中引用它,就像引用任何局部变量一样。
访问的变量不仅包括定义为数据类型的变量,还包括记录类型、集合类型和游标等其他变量。
这些变量最多可由一个限定符访问,该限定符是其中已本地声明这些变量的子程序的名称或带标记的匿名块。
引用变量的语法如下所示:
[qualifier.]variable
如果指定,qualifier 是子程序或带标记的匿名块,variable 已在其声明部分中进行声明(即,它是局部变量)。
在PolarDB PostgreSQL版(兼容Oracle)中,只有一种情况允许使用两个限定符。这种情况用于访问包的公共变量,其中可按以下格式指定引用:
schema_name.package_name.public_variable_name
下面总结了访问变量的方式:
- 只要变量已在其中进行本地声明的块位于从包含变量引用的块开始的祖先分层路径中,就可以访问变量。祖先块中声明的此类变量称为全局变量。
- 在引用非限定变量时,首先将尝试找到该名称的局部变量。如果这样的局部变量不存在,则在当前块的父级中搜索该变量,依此类推,将沿祖先层次结构向上继续搜索。如果未找到此类变量,则在调用子程序时会发生错误。
- 如果引用了一个限定变量,将按照前面要点中的描述执行相同的搜索过程,但是在包含局部变量的子程序或带标记的匿名块中搜索第一个匹配项。将沿祖先层次结构继续向上搜索,直到找到匹配项。如果未找到这样的匹配项,则在调用子程序时会发生错误。
以下变量位置(相对于引用变量的块)无法访问:
- 后代块中声明的变量无法访问。
- 同辈块、祖先块的同辈块或同辈块内的任何后代中声明的变量无法访问。
以下示例显示如何使用限定符和不使用限定符访问各种块中的变量。注释掉的行阐释了将导致错误的变量访问尝试。
CREATE OR REPLACE PROCEDURE level_0
IS
v_level_0 VARCHAR2(20) := 'Value from level_0';
PROCEDURE level_1a
IS
v_level_1a VARCHAR2(20) := 'Value from level_1a';
PROCEDURE level_2a
IS
v_level_2a VARCHAR2(20) := 'Value from level_2a';
BEGIN
DBMS_OUTPUT.PUT_LINE('...... BLOCK level_2a');
DBMS_OUTPUT.PUT_LINE('........ v_level_2a: ' || v_level_2a);
DBMS_OUTPUT.PUT_LINE('........ v_level_1a: ' || v_level_1a);
DBMS_OUTPUT.PUT_LINE('........ level_1a.v_level_1a: ' ||
level_1a.v_level_1a);
DBMS_OUTPUT.PUT_LINE('........ v_level_0: ' || v_level_0);
DBMS_OUTPUT.PUT_LINE('........ level_0.v_level_0: ' || level_0.v_level_0);
DBMS_OUTPUT.PUT_LINE('...... END BLOCK level_2a');
END level_2a;
BEGIN
DBMS_OUTPUT.PUT_LINE('.. BLOCK level_1a');
level_2a;
-- DBMS_OUTPUT.PUT_LINE('.... v_level_2a: ' || v_level_2a);
-- Error - Descendent block ----^
-- DBMS_OUTPUT.PUT_LINE('.... level_2a.v_level_2a: ' || level_2a.v_level_2a);
-- Error - Descendent block ---------------^
DBMS_OUTPUT.PUT_LINE('.. END BLOCK level_1a');
END level_1a;
PROCEDURE level_1b
IS
v_level_1b VARCHAR2(20) := 'Value from level_1b';
BEGIN
DBMS_OUTPUT.PUT_LINE('.. BLOCK level_1b');
DBMS_OUTPUT.PUT_LINE('.... v_level_1b: ' || v_level_1b);
DBMS_OUTPUT.PUT_LINE('.... v_level_0 : ' || v_level_0);
-- DBMS_OUTPUT.PUT_LINE('.... level_1a.v_level_1a: ' || level_1a.v_level_1a);
-- Error - Sibling block -----------------^
-- DBMS_OUTPUT.PUT_LINE('.... level_2a.v_level_2a: ' || level_2a.v_level_2a);
-- Error - Sibling block descendant ------^
DBMS_OUTPUT.PUT_LINE('.. END BLOCK level_1b');
END level_1b;
BEGIN
DBMS_OUTPUT.PUT_LINE('BLOCK level_0');
DBMS_OUTPUT.PUT_LINE('.. v_level_0: ' || v_level_0);
level_1a;
level_1b;
DBMS_OUTPUT.PUT_LINE('END BLOCK level_0');
END level_0;
下面是输出,显示了调用存储过程时每个变量的内容:
BEGIN
level_0;
END;
BLOCK level_0
.. v_level_0: Value from level_0
.. BLOCK level_1a
...... BLOCK level_2a
........ v_level_2a: Value from level_2a
........ v_level_1a: Value from level_1a
........ level_1a.v_level_1a: Value from level_1a
........ v_level_0: Value from level_0
........ level_0.v_level_0: Value from level_0
...... END BLOCK level_2a
.. END BLOCK level_1a
.. BLOCK level_1b
.... v_level_1b: Value from level_1b
.... v_level_0 : Value from level_0
.. END BLOCK level_1b
END BLOCK level_0
以下示例显示所有块中的所有变量具有相同名称时类似的访问尝试:
CREATE OR REPLACE PROCEDURE level_0
IS
v_common VARCHAR2(20) := 'Value from level_0';
PROCEDURE level_1a
IS
v_common VARCHAR2(20) := 'Value from level_1a';
PROCEDURE level_2a
IS
v_common VARCHAR2(20) := 'Value from level_2a';
BEGIN
DBMS_OUTPUT.PUT_LINE('...... BLOCK level_2a');
DBMS_OUTPUT.PUT_LINE('........ v_common: ' || v_common);
DBMS_OUTPUT.PUT_LINE('........ level_2a.v_common: ' || level_2a.v_common);
DBMS_OUTPUT.PUT_LINE('........ level_1a.v_common: ' || level_1a.v_common);
DBMS_OUTPUT.PUT_LINE('........ level_0.v_common: ' || level_0.v_common);
DBMS_OUTPUT.PUT_LINE('...... END BLOCK level_2a');
END level_2a;
BEGIN
DBMS_OUTPUT.PUT_LINE('.. BLOCK level_1a');
DBMS_OUTPUT.PUT_LINE('.... v_common: ' || v_common);
DBMS_OUTPUT.PUT_LINE('.... level_0.v_common: ' || level_0.v_common);
level_2a;
DBMS_OUTPUT.PUT_LINE('.. END BLOCK level_1a');
END level_1a;
PROCEDURE level_1b
IS
v_common VARCHAR2(20) := 'Value from level_1b';
BEGIN
DBMS_OUTPUT.PUT_LINE('.. BLOCK level_1b');
DBMS_OUTPUT.PUT_LINE('.... v_common: ' || v_common);
DBMS_OUTPUT.PUT_LINE('.... level_0.v_common : ' || level_0.v_common);
DBMS_OUTPUT.PUT_LINE('.. END BLOCK level_1b');
END level_1b;
BEGIN
DBMS_OUTPUT.PUT_LINE('BLOCK level_0');
DBMS_OUTPUT.PUT_LINE('.. v_common: ' || v_common);
level_1a;
level_1b;
DBMS_OUTPUT.PUT_LINE('END BLOCK level_0');
END level_0;
下面是输出,显示了调用存储过程时每个变量的内容:
BEGIN
level_0;
END;
BLOCK level_0
.. v_common: Value from level_0
.. BLOCK level_1a
.... v_common: Value from level_1a
.... level_0.v_common: Value from level_0
...... BLOCK level_2a
........ v_common: Value from level_2a
........ level_2a.v_common: Value from level_2a
........ level_1a.v_common: Value from level_1a
........ level_0.v_common: Value from level_0
...... END BLOCK level_2a
.. END BLOCK level_1a
.. BLOCK level_1b
.... v_common: Value from level_1b
.... level_0.v_common : Value from level_0
.. END BLOCK level_1b
END BLOCK level_0
如上所述,匿名块上的标签也可用于限定对变量的访问。以下示例显示一组嵌套匿名块中的变量访问:
DECLARE
v_common VARCHAR2(20) := 'Value from level_0';
BEGIN
DBMS_OUTPUT.PUT_LINE('BLOCK level_0');
DBMS_OUTPUT.PUT_LINE('.. v_common: ' || v_common);
<<level_1a>>
DECLARE
v_common VARCHAR2(20) := 'Value from level_1a';
BEGIN
DBMS_OUTPUT.PUT_LINE('.. BLOCK level_1a');
DBMS_OUTPUT.PUT_LINE('.... v_common: ' || v_common);
<<level_2a>>
DECLARE
v_common VARCHAR2(20) := 'Value from level_2a';
BEGIN
DBMS_OUTPUT.PUT_LINE('...... BLOCK level_2a');
DBMS_OUTPUT.PUT_LINE('........ v_common: ' || v_common);
DBMS_OUTPUT.PUT_LINE('........ level_1a.v_common: ' || level_1a.v_common);
DBMS_OUTPUT.PUT_LINE('...... END BLOCK level_2a');
END;
DBMS_OUTPUT.PUT_LINE('.. END BLOCK level_1a');
END;
<<level_1b>>
DECLARE
v_common VARCHAR2(20) := 'Value from level_1b';
BEGIN
DBMS_OUTPUT.PUT_LINE('.. BLOCK level_1b');
DBMS_OUTPUT.PUT_LINE('.... v_common: ' || v_common);
DBMS_OUTPUT.PUT_LINE('.... level_1b.v_common: ' || level_1b.v_common);
DBMS_OUTPUT.PUT_LINE('.. END BLOCK level_1b');
END;
DBMS_OUTPUT.PUT_LINE('END BLOCK level_0');
END;
下面是输出,显示了调用匿名块时每个变量的内容:
BLOCK level_0
.. v_common: Value from level_0
.. BLOCK level_1a
.... v_common: Value from level_1a
...... BLOCK level_2a
........ v_common: Value from level_2a
........ level_1a.v_common: Value from level_1a
...... END BLOCK level_2a
.. END BLOCK level_1a
.. BLOCK level_1b
.... v_common: Value from level_1b
.... level_1b.v_common: Value from level_1b
.. END BLOCK level_1b
END BLOCK level_0
以下示例是一个对象类型,其对象类型方法 display_emp 包含记录类型 emp_typ 和子存储过程 emp_sal_query。本地声明为 emp_sal_query 的记录变量 r_emp 能够访问父块 display_emp 中声明的记录类型 emp_typ。
CREATE OR REPLACE TYPE emp_pay_obj_typ AS OBJECT
(
empno NUMBER(4),
MEMBER PROCEDURE display_emp(SELF IN OUT emp_pay_obj_typ)
);
CREATE OR REPLACE TYPE BODY emp_pay_obj_typ AS
MEMBER PROCEDURE display_emp (SELF IN OUT emp_pay_obj_typ)
IS
TYPE emp_typ IS RECORD (
ename emp.ename%TYPE,
job emp.job%TYPE,
hiredate emp.hiredate%TYPE,
sal emp.sal%TYPE,
deptno emp.deptno%TYPE
);
PROCEDURE emp_sal_query (
p_empno IN emp.empno%TYPE
)
IS
r_emp emp_typ;
v_avgsal emp.sal%TYPE;
BEGIN
SELECT ename, job, hiredate, sal, deptno
INTO r_emp.ename, r_emp.job, r_emp.hiredate, r_emp.sal, r_emp.deptno
FROM emp WHERE empno = p_empno;
DBMS_OUTPUT.PUT_LINE('Employee # : ' || p_empno);
DBMS_OUTPUT.PUT_LINE('Name : ' || r_emp.ename);
DBMS_OUTPUT.PUT_LINE('Job : ' || r_emp.job);
DBMS_OUTPUT.PUT_LINE('Hire Date : ' || r_emp.hiredate);
DBMS_OUTPUT.PUT_LINE('Salary : ' || r_emp.sal);
DBMS_OUTPUT.PUT_LINE('Dept # : ' || r_emp.deptno);
SELECT AVG(sal) INTO v_avgsal
FROM emp WHERE deptno = r_emp.deptno;
IF r_emp.sal > v_avgsal THEN
DBMS_OUTPUT.PUT_LINE('Employee''s salary is more than the '
|| 'department average of ' || v_avgsal);
ELSE
DBMS_OUTPUT.PUT_LINE('Employee''s salary does not exceed the '
|| 'department average of ' || v_avgsal);
END IF;
END;
BEGIN
emp_sal_query(SELF.empno);
END;
END;
下面是创建该对象类型的实例并调用存储过程 display_emp 时显示的输出:
DECLARE
v_emp EMP_PAY_OBJ_TYP;
BEGIN
v_emp := emp_pay_obj_typ(7900);
v_emp.display_emp;
END;
Employee # : 7900
Name : JAMES
Job : CLERK
Hire Date : 03-DEC-81 00:00:00
Salary : 950.00
Dept # : 30
Employee's salary does not exceed the department average of 1566.67
以下示例是一个具有三个层级的子存储过程的包。后代子存储过程可访问上层存储过程中声明的记录类型、集合类型和游标类型。
CREATE OR REPLACE PACKAGE emp_dept_pkg
IS
PROCEDURE display_emp (
p_deptno NUMBER
);
END;
CREATE OR REPLACE PACKAGE BODY emp_dept_pkg
IS
PROCEDURE display_emp (
p_deptno NUMBER
)
IS
TYPE emp_rec_typ IS RECORD (
empno emp.empno%TYPE,
ename emp.ename%TYPE
);
TYPE emp_arr_typ IS TABLE OF emp_rec_typ INDEX BY BINARY_INTEGER;
TYPE emp_cur_type IS REF CURSOR RETURN emp_rec_typ;
PROCEDURE emp_by_dept (
p_deptno emp.deptno%TYPE
)
IS
emp_arr emp_arr_typ;
emp_refcur emp_cur_type;
i BINARY_INTEGER := 0;
PROCEDURE display_emp_arr
IS
BEGIN
DBMS_OUTPUT.PUT_LINE('EMPNO ENAME');
DBMS_OUTPUT.PUT_LINE('----- -------');
FOR j IN emp_arr.FIRST .. emp_arr.LAST LOOP
DBMS_OUTPUT.PUT_LINE(emp_arr(j).empno || ' ' ||
emp_arr(j).ename);
END LOOP;
END display_emp_arr;
BEGIN
OPEN emp_refcur FOR SELECT empno, ename FROM emp WHERE deptno = p_deptno;
LOOP
i := i + 1;
FETCH emp_refcur INTO emp_arr(i).empno, emp_arr(i).ename;
EXIT WHEN emp_refcur%NOTFOUND;
END LOOP;
CLOSE emp_refcur;
display_emp_arr;
END emp_by_dept;
BEGIN
emp_by_dept(p_deptno);
END;
END;
下面是调用顶层包存储过程时显示的输出:
BEGIN
emp_dept_pkg.display_emp(20);
END;
EMPNO ENAME
----- -------
7369 SMITH
7566 JONES
7788 SCOTT
7876 ADAMS
7902 FORD