17. B PL/SQL名称解析
本附录解释 PL/SQL名称解析;也就是说,PL/SQL 编译器如何解析对标识符的模糊引用。
如果您在其编译单元中更改标识符(即如果您添加、重命名或删除标识符),则明确的标识符引用可能会变得不明确。
注意
存储的 PL/SQL 单元的AUTHID属性会影响该单元在运行时发出的 SQL 语句的名称解析。有关详细信息,请参阅“调用者的权限和定义者的权限(AUTHID 属性)”。
17.1. 限定名称和点符号
当一个命名项属于另一个命名项时,您可以(有时必须)使用点表示法用“父”项的名称来限定“子”项的名称。例如:
引用 | 名称限定 | 语法 |
---|---|---|
记录的字段 | 记录名称 | 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
一个简单的名称可以用多个名称来限定,如:ref:`示例 B-1`所示。
可能有歧义的限定名称的一些示例是: - 函数返回值的字段或属性,例如:
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 ()
示例 B-1 限定名称
\set SQLTERM / CREATE OR REPLACE PACKAGE pkg1 AUTHID DEFINER AS num NUMBER; TYPE rt IS RECORD (a NUMBER); var1 rt; TYPE ct IS TABLE OF rt INDEX BY PLS_INTEGER; var2 ct; FUNCTION f1 (a1 NUMBER) RETURN rt; FUNCTION f2 (a2 NUMBER) RETURN ct; END pkg1; / CREATE OR REPLACE PACKAGE BODY pkg1 AS FUNCTION f1 (a1 NUMBER) RETURN rt IS n NUMBER; BEGIN n := num; -- 不合格的变量名 n := pkg1.num; -- 包名限定的变量名 n := pkg1.f1.a1; -- 函数名限定的参数名,由包名限定 n := var1.a; -- 变量名后跟字段名 n := pkg1.var1.a; -- 包名限定的变量名后跟组件名称 var1.a := a1; RETURN var1; END f1; FUNCTION f2 (a2 NUMBER) RETURN ct IS v_rt rt; v_ct ct; BEGIN v_rt.a := a2; v_ct(1) := v_rt; RETURN v_ct; END f2; END pkg1; /
17.2. 列名优先级
如果 SQL 语句引用的名称既属于列又属于局部变量或形参,则列名优先。
警告
当变量或参数名称被解释为列名称时,可能会无意中删除、更改或插入数据。
在:ref:`示例 B-2`中,名称 name 既属于局部变量又属于列(名称不区分大小写)。因此,在 WHERE 子句中,对 name 的两个引用都解析为列,并且所有行都被删除。
:ref:`示例 B-3`通过给变量一个不同的名称 来解决:ref:`示例 B-2`中的问题。
在:ref:`示例 B-4`中,函数 dept_name 有一个形式参数和一个局部变量,其名称是表 DEPARTMENTS 的列的名称。参数和变量名用函数名限定,以区别于列名。
示例 B-2 解释为列名的变量名导致意外结果
DROP TABLE IF EXISTS student; CREATE TABLE student (id int PRIMARY KEY, name text, score number); insert into student values (1, 'xx', 99); CREATE TABLE stu_name AS SELECT name FROM student; \set SQLTERM / DECLARE name TEXT := 'xa'; BEGIN DELETE FROM student WHERE name = name; RAISE NOTICE 'Deleted % rows', SQL%ROWCOUNT; END; /
结果:
NOTICE: Deleted 1 rows
示例 B-3使用不同的变量名称 修复示例 B-2
insert into student values (1, 'xx', 99); \set SQLTERM / DECLARE v_name TEXT := 'xa'; BEGIN DELETE FROM student WHERE name = v_name; RAISE NOTICE 'Deleted % rows', SQL%ROWCOUNT; END; /
结果:
NOTICE: Deleted 0 rows
示例 B-4 名称解析的子程序名称
\set SQLTERM / DECLARE FUNCTION stu_name (id IN NUMBER) RETURN student.name%TYPE AS DECLARE v_name student.name%TYPE; BEGIN SELECT name INTO v_name FROM student WHERE id = stu_name.id; RETURN v_name; END stu_name; BEGIN FOR item IN ( SELECT id FROM student) LOOP RAISE NOTICE 'Student: %', stu_name(item.id); END LOOP; END; /
结果:
NOTICE: Student: xx
17.3. PL/SQL 和 SQL 名称解析规则的区别
PL/SQL 和 SQL 名称解析规则非常相似。然而: - PL/SQL 规则比 SQL 规则更宽松。
因为大多数 SQL 规则是上下文相关的,它们比 PL/SQL 规则识别更多的情况是合法的。
PL/SQL 和 SQL 以不同的方式解析限定名。
例如,在解析表名时student:
PL/SQL 首先搜索 ms 当前模式中命名的包、类型、表和视图,然后搜索公共同义词,最后在 ms 模式中搜索名为 JOBS 的对象。
SQL 首先在 ms 模式中搜索名为 JOBS 的对象,然后在当前模式中搜索名为 ms 的包、类型、表和视图。
为避免由于 PL/SQL 和 SQL 名称解析规则之间的少量差异而导致的问题,请遵循“避免 SELECT 和 DML 语句中的内部捕获”中的建议。
注意
当 PL/SQL 编译器处理静态 SQL 语句时,它将该语句发送到 SQL 子系统,该子系统使用 SQL 规则来解析语句中的名称。详见“静态SQL语句中名称的解析”。
17.4. 静态 SQL 语句中的名称解析
静态 SQL 在PL/SQL 静态 SQL中进行了描述。
当 PL/SQL 编译器找到静态 SQL 语句时:
如果语句是SELECT语句,则 PL/SQL 编译器删除INTO子句。
PL/SQL 编译器将语句发送到 SQL 子系统。
SQL 子系统检查语句的语法。
如果语法不正确,PL/SQL 单元的编译将失败。如果语法正确,SQL 子系统将确定表的名称并尝试解析 SQL 语句范围内的其他名称。
如果 SQL 子系统无法解析 SQL 语句范围内的名称,则它将名称发送回 PL/SQL 编译器。该名称称为转义标识符。
PL/SQL 编译器尝试解析转义的标识符。
首先,编译器尝试解析 PL/SQL 单元范围内的标识符。如果失败,编译器会尝试解析模式范围内的标识符。如果失败,PL/SQL 单元的编译就会失败。
如果 PL/SQL 单元的编译成功,则 PL/SQL 编译器生成与静态 SQL 语句等效的常规 SQL 语句的文本,并将该文本与生成的计算机代码一起存储。
在运行时,PL/SQL 运行时系统调用解析、绑定和运行常规 SQL 语句的例程。
绑定变量是转义的标识符(参见步骤 4)。
如果语句是 SELECT 语句,则 PL/SQL 运行时系统将结果存储在 PL/SQL 编译器在步骤 1 中删除的 INTO 子句中指定的 PL/SQL 目标中。
注意
绑定变量可以按任何顺序计算。如果一个程序确定了评估的顺序,那么在程序这样做的时候,它的行为是未定义的。