在 Oracle PL/SQL 块中,为什么允许动态 sql
begin
execute immediate 'drop table table_name';
end;
但静态不是吗?
begin
drop table table_name;
end;
我希望答案比“因为这就是语言的工作原理”更有洞察力。
答案是 PL/SQL 不支持动态多态性。它只支持静态多态,因为
所有 PL/SQL 都会为 Ada 生成一个“DIANA”-> Descriptive Intermediate Attributed Notation,这是一种树形结构的中间语言。编译器在内部使用 DIANA。
在编译时,PL/SQL 源代码被翻译成系统代码并生成相应的 DIANA。现在想想如果有一个像 create table 语句这样的 DDL 语句在编译时不存在,它将在运行程序后创建。那么您的 PL/SQL 引擎将如何生成 DIANA 呢????
DIANA 在 PL/SQL 中起着重要的作用,用于检查/验证子程序。这是必需的,因为我们知道子程序可以使用数据库对象,例如表、视图、同义词或其他存储过程。下次运行程序时,对象可能已更改/删除/丢弃。例如:有人可能删除了表,存储的过程或函数签名可能已更改。
这就是为什么通常使用 PL/SQL 来操纵数据库结构中的数据,而不是操纵那些结构的原因。
但是有一些方法可以使用动态 SQL 和 DBMS_SQL 包进行操作,但这些方法再次应谨慎使用。例如,如果你正在创建一个表,你应该首先检查这个表是否已经存在或者没有使用数据字典视图。
可能是因为否则一些代码会像:
begin
create table tmp (n number);
insert into tmp values (1);
end;
我们希望编译器知道在插入时该表存在。块的编译将使我更加困难。这是一个非常简单的案例,但我们可以很容易地想象一些条件分支,以及复杂的blabla。
但是,由于我们需要将 DDL 放在立即执行块中,因此限制可能更容易理解。
只是一个想法...
在执行/编译之前,oracle 检查在 pl/sql 块中引用的所有模式对象(如表、视图、存储过程等)的所有访问权限、有效性和依赖关系。但是 DDL 语句的问题在于它可以创建、更改或删除模式对象,因此它可以更改对象依赖关系。因此,我们可能指的是一个已使用 DDL 删除的对象。为了防止这种情况,我猜 plsql 块不允许直接 DDL 语句。但是我们仍然可以使用动态查询在 PL/SQL 块中包含 DDL。由于在这种情况下实际查询直到运行时才知道,基本上我们可以以动态 SQL 的形式隐藏 DDL 语句,并将 DDL 包含在 PL/SQL 块中。
您可以参考我的博客以通过示例来理解这个概念: Why oracle does not allow direct DDL statements inside the procedure (PLSQL BLOCK)