添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

--====================

使用动态 SQL 是在编写 PL / SQL 过程时经常使用的方法之一。很多情况下,比如根据业务的需要,如果输入不同查询条件,则生成不同的执行

SQL 查询语句,对于这种情况需要使用动态 SQL 来完成。再比如,对于分页的情况,对于不同的表,必定存在不同的字段,因此使用静态 SQL 则只

能针对某几个特定的表来形成分页。而使用动态的 SQL ,则可以对不同的表,不同的字段进行不同的分页。这些情况的处理通常都是用动态 SQL

完成。本文讲述了动态 SQL 的日常用法。

一、动态 SQL 和静态 SQL

1. 静态 SQL

静态 SQL 通常用于完成可以确定的任务。比如传递部门号调用存储过程,返回该部门的所有雇员及薪水信息,则该语句为

SELECT ename sal INTO lv_ename , lv_sal FROM scott . emp WHERE deptno =& dno ;

对于上述类似的 DML 语句在第一次运行时进行编译,而后续再次调用,则不再编译该过程。即一次编译,多次调用,使用的相同的执行

计划。此种方式被称之为使用的是静态的 SQL

2. 动态 SQL

动态 SQL 通常是用来根据不同的需求完成不同的任务。比如分页查询,对于表 emp 分页,需要使用字段雇员姓名,薪水,雇用日期,且按

薪水降序生成报表,每页显示行数据。而对于表 sales ,需要使用字段雇员名称,客户名称,销售数量,销售日期,且按销售日期升序

排列。以上两种情况,可以创建存储过程来对其进行分页,通过定义变量,根据输入不同的表名,字段名,排序方法来生成不同的 SQL

语句。对于输入不同的参数, SQL 在每次运行时需要事先对其编译。即多次调用则需要多次编译,此称之为动态 SQL

动态 SQL 语句通常存放在字符串变量中,且 SQL 语句可以包含占位符 ( 使用冒号开头 )

也可以直接将动态 SQL 紧跟在 EXECUTE IMMEDIATE 语句之后,如 EXECUTE IMMEDIATE 'alter table emp enable row movement'

3. 两者的异同

静态 SQL 为直接嵌入到 PL / SQL 中的代码,而动态 SQL 在运行时,根据不同的情况产生不同的 SQL 语句。

静态 SQL 为在执行前编译,一次编译,多次运行。动态 SQL 同样在执行前编译,但每次执行需要重新编译。

静态 SQL 可以使用相同的执行计划,对于确定的任务而言,静态 SQL 更具有高效性。但缺乏灵活性

动态 SQL 使用了不同的执行计划,效率不如静态 SQL ,但能够解决复杂的问题。

动态 SQL 容易产生 SQL 注入,为数据库安全带来隐患。

4. 动态 SQL 语句的几种方法

a. 使用 EXECUTE IMMEDIATE 语句

包括 DDL 语句, DCL 语句, DML 语句以及单行的 SELECT 语句。该方法不能用于处理多行查询语句。

b. 使用 OPEN-FOR FETCH CLOSE 语句

对于处理动态多行的查询操作,可以使用 OPEN-FOR 语句打开游标,使用 FETCH 语句循环提取数据,最终使用 CLOSE 语句关闭游标。

c. 使用批量动态 SQL

即在动态 SQL 中使用 BULK 子句,或使用游标变量时在 fetch 中使用 BULK ,或在 FORALL 语句中使用 BULK 子句来实现。

d. 使用系统提供的 PL/SQL DBMS_SQL 来实现动态 SQL ,关于该方式请参考后续博文。

二、动态 SQL 的语法

下面是动态 SQL 常用的语法之一

EXECUTE IMMEDIATE dynamic_SQL_string

[INTO defined_variable1, defined_variable2, ...]

[USING [IN | OUT | IN OUT] bind_argument1 , bind_argument2 ,

... ][{RETURNING | RETURN} field1, field2, ... INTO bind_argument1,

bind_argument2, ...]

1. 语法描述

dynamic_SQL_string :存放指定的 SQL 语句或 PL / SQL 块的字符串变量

defined_variable1 :用于存放单行查询结果,使用时必须使用 INTO 关键字,类似于使用 SELECT ename INTO v_name FROM scott . emp

只不过在动态 SQL 时,将 INTO defined_variable1 移出到 dynamic_SQL_string 语句之外。

bind_argument1 :用于给动态 SQL 语句传入或传出参数,使用时必须使用 USING 关键字, IN 表示传入的参数, OUT 表示传出的参数,

IN OUT 则既可以传入,也可传出。

RETURNING | RETURN 子句也是存放 SQL 动态返回值的变量。

2. 使用要点

a.EXECUTE IMMEDIATE 执行 DML 时,不会提交该 DML 事务,需要使用显示提交 (COMMIT) 或作为 EXECUTE IMMEDIATE 自身的一部分。

b.EXECUTE IMMEDIATE 执行 DDL,DCL 时会自动提交其执行的事务。

c. 对于多行结果集的查询,需要使用游标变量或批量动态 SQL ,或者使用临时表来实现。

d. 当执行 SQL 时,其尾部不需要使用分号,当执行 PL/SQL 代码时,其尾部需要使用分号。

f . 动态 SQL 中的占位符以冒号开头,紧跟任意字母或数字表示。

三、动态 SQL 的使用 ( DDL DCL DML 以及单行结果集 )

1. 使用 EXECUTE IMMEDIATE 处理 DDL 操作

下面是一个简单的 DDL 操作,将其封装在存储过程之中,通过传入表名来进行调用。

CREATE OR REPLACE PROCEDURE trunc_table ( table_name VARCHAR2 ) -- 创建存储过程 trunc_table

sql_statement VARCHAR2 ( 100 );

BEGIN

sql_statement := 'TRUNCATE TABLE ' || table_name ; -- 为变量进行赋值,用于生成动态 SQL 语句

EXECUTE IMMEDIATE sql_statement ; -- 使用 EXECUTE IMMEDIATE 执行动态 SQL 语句

END ;

flasher@ORCL > create table tb2 -- scott.emp 生产表 tb2

2 as select empno , ename , sal , deptno from scott . emp ;

flasher@ORCL > select count ( 1 ) from tb2 ;

COUNT ( 1 )

----------

flasher@ORCL > exec trunc_table ( 'tb2' ); -- 调用存储过程来对表 tb2 进行 truncate

flasher@ORCL > select count ( 1 ) from tb2 ; -- tb2 被清空

COUNT ( 1 )

----------

flasher@ORCL > insert into tb2 -- 重新为表 tb2 生成记录

2 select empno , ename , sal , deptno from scott . emp ;

flasher@ORCL > commit ;

2. 使用 EXECUTE IMMEDIATE 处理 DCL 操作

下面使用 sys 帐户创建存储过程 grant_sys_priv 用于给用户授予权限

sys@ORCL > conn sys / redhat@orcl as sysdba

CREATE OR REPLACE PROCEDURE grant_sys_priv ( priv VARCHAR2 , username VARCHAR2 )

sql_stat VARCHAR2 ( 100 );

BEGIN

sql_stat := 'GRANT ' || priv || ' TO ' || username ;

EXECUTE IMMEDIATE sql_stat ;

END ;

sys@ORCL > exec grant_sys_priv ( 'connect' , 'usr1' );

3. 使用 EXECUTE IMMEDIATE 处理 DML 操作

在使用 EXECUTE IMMEDIATE 处理 DML 操作时,分为几种情况,即不带输入参数,带输入参数,既有输入也有输出参数或返回参数等不同情

况,下面分别对其描述。

a . 没有参数传入传出的 DML 语句

下面的示例中,使用动态 SQL 删除一条记录,且未使用参数传入。

flasher@ORCL > select * from tb2 where empno = 7900 ; -- 删除前

EMPNO ENAME SAL DEPTNO

---------- ---------- ---------- ----------

7900 JAMES 950 30

flasher@ORCL > DECLARE sql_stat VARCHAR2 ( 100 );

2 BEGIN

3 sql_stat := 'DELETE FROM flasher.tb2 WHERE empno=7900' ; -- 使用动态 SQL 来删除记录

4 EXECUTE IMMEDIATE sql_stat ;

5 END ;

6 /

flasher@ORCL > SELECT * FROM tb2 where empno = 7900 ; -- 验证删除情况

no rows selected

b . 有参数传入的 DML 语句 ( 使用 USING 子句 )

对于使用了参数传入的动态 SQL ,需要使用 USING 子句来指明传入的参数。在下面的示例中,为表 tb2 插入一条记录,在 DML 语句中使

用了四个占位符 ( 占位符用以冒号开头,紧跟任意字母或数字表示 ) 。因此在使用 EXECUTE IMMEDIATE 使用 USING 子句为其指定其参数。

DECLARE -- 声明变量

sql_stat VARCHAR2 ( 100 );

lv_empno tb2 . empno % TYPE := 7900 ;

lv_ename tb2 . ename % TYPE := 'JAMES' ;

lv_sal tb2 . sal % TYPE := 950 ;

BEGIN

sql_stat := 'INSERT INTO tb2 VALUES(:1,:2,:3,:4)' ; --DML 语句中使用了占位符

EXECUTE IMMEDIATE sql_stat USING lv_empno , lv_ename , lv_sal , 30 ; -- 为占位符指定参数或值

COMMIT ;

END ;

flasher@ORCL > select * from tb2 where empno = 7900 ; -- 验证插入后的结果

EMPNO ENAME SAL DEPTNO

---------- ---------- ---------- ----------

7900 JAMES 950 30

c . 处理包含 returning 子句的 DML 语句

下面的示例中,对表 tb2 进行更新,使用了两个占位符,一个是 :percent ,一个是 :eno ,因此在使用 EXECUTE IMMEDIATE 执行动态

DML 时,需要使用 USING 子句且带两个输入参数。其次,动态 DML 中使用了 RETURNING sal INTO :salary ,因此 EXECUTE IMMEDIATE

也必须使用 RETURNING INTO varialbe_name

DECLARE

salary NUMBER ( 6 , 2 );

sql_stat VARCHAR2 ( 100 );

BEGIN

sql_stat := 'UPDATE tb2 SET sal = sal * (1 + :percent / 100)' -- 更新 sal 列,使用占位符 :percent

|| ' WHERE empno = :eno RETURNING sal INTO :salary' ; -- 使用了占位符 :eno :salary ,以及 RETURNING 子句

EXECUTE IMMEDIATE sql_stat USING & 1 , & 2 RETURNING INTO salary ; -- 必须使用 USING RETURNING 子句

COMMIT ;

dbms_output . put_line ( 'New salary: ' || salary );

END ;

Enter value for 1 : 10

Enter value for 2 : 7900

old 7 : EXECUTE IMMEDIATE sql_stat USING & 1 , & 2 RETURNING INTO salary ;

new 7 : EXECUTE IMMEDIATE sql_stat USING 10 , 7900 RETURNING INTO salary ;

New salary : 1045

d . 处理包含检索值的单行查询

下面的示例中,使用 SELECT 查询获得单行结果集,使用了占位符 :name ,因此也需要使用 USING 子句为其传递参数

DECLARE

sql_stat VARCHAR2 ( 100 );

emp_record tb2 % ROWTYPE ;

BEGIN

sql_stat := 'SELECT * FROM tb2 WHERE ename = UPPER(:name)' ; -- 动态 SQL 语句为单行 DQL 语句

EXECUTE IMMEDIATE sql_stat INTO emp_record USING '&name' ; -- 使用 USING 子句为其传递参数

DBMS_OUTPUT . PUT_LINE ( 'The salary is ' || emp_record . sal || ' for ' || emp_record . ename );

END ;

Enter value for 1 : james

old 6 : EXECUTE IMMEDIATE sql_stat INTO emp_record USING '&1' ;

new 6 : EXECUTE IMMEDIATE sql_stat INTO emp_record USING 'james' ;

The salary is 1045 for JAMES

四、动态 SQL 的使用 ( 处理多行结果集的查询语句 )

1. 使用游标变量来循环提取数据,其主要流程为

定义游标变量

TYPE cursortype IS REF CURSOR ;

cursor_variable cursortype ;

打开游标变量

OPEN cursor_variable FOR dynamic_string

[USING bind_argument[,bind_argument] ... ]

循环提取数据

FETCH cursor_variable INTO { var1[,var2] ...| record_variable} ;

EXIT WHEN cursor_variable % NOTFOUND

关闭游标变量

CLOSE cursor_variable ;

2. 使用游标变量处理查询多行结果集

下面的示例中,首先定义了一个游标类型,接下来定义游标变量,以及存放结果集的变量,动态查询语句将获得多个结果集。

OPEN cursorname FOR SELECT ... 时,其 SELECT 语句使用了字符串变量 ( 动态 SQL ) ,其后紧跟 USING 子句。

DECLARE -- 游标,变量的声明

TYPE emp_cur_type IS REF CURSOR ;

emp_cv emp_cur_type ;

emp_record tb2 % ROWTYPE ;

sql_stat VARCHAR2 ( 100 );

v_dno NUMBER := & dno ;

BEGIN

sql_stat := 'SELECT * FROM tb2 WHERE deptno = :dno' ; -- 动态多行结果集查询语句

OPEN emp_cv FOR sql_stat USING v_dno ; --OPEN 时使用动态查询语句以及 USING 子句来传递参数

FETCH emp_cv INTO emp_record ; -- 从结果集中提取记录

EXIT WHEN emp_cv % NOTFOUND ;

dbms_output . put_line ( 'Employee name ' || emp_record . ename || ', Salary ' || emp_record . sal );

END LOOP ;

CLOSE emp_cv ;

END ;

Employee name Henry , Salary

Employee name JONES , Salary

Employee name ADAMS , Salary

Employee name FORD , Salary

五、动态 SQL 的使用 (FORALL BULK 子句的使用 )

1. 动态 SQL 中使用 BULK 子句的语法

EXECUTE IMMEDIATE dynamic_string --dynamic_string 用于存放动态 SQL 字符串

[BULK COLLECT INTO define_variable[,define_variable...]] -- 存放查询结果的集合变量

[USING bind_argument[,argument...]] -- 使用参数传递给动态 SQL

[{RETURNING | RETURN} -- 返回子句

BULK COLLECT INTO return_variable[,return_variable...]]; -- 存放返回结果的集合变量

使用 bulk collect into 子句处理动态 SQL 中的多行查询可以加快处理速度,从而提高应用程序的性能。当使用 bulk 子句时,集合类型可

以是 PL/SQL 所支持的索引表、嵌套表和 VARRY ,但集合元素必须使用 SQL 数据类型。常用的三种语句支持 BULK 子句,分别为 EXECUTE

IMMEDIATE FETCH FORALL

2. 使用 EXECUTE IMMEDIATE 结合 BULK 子句处理 DML 语句返回子句

下面的例子,首先定义了两个索引表类型以及其变量,接下来使用动态 SQL 语句来更新 tb2 的薪水,使用 EXECUTE IMMEDIATE 配合 BULK

COLLECT INTO 来处理结果集。

DECLARE

TYPE ename_table_type IS TABLE OF tb2 . ename % TYPE INDEX BY BINARY_INTEGER ; -- 定义类型用于存放结果集

TYPE sal_table_type IS TABLE OF tb2 . sal % TYPE INDEX BY BINARY_INTEGER ;

ename_table ename_table_type ;

sal_table sal_table_type ;

sql_stat VARCHAR2 ( 120 );

v_percent NUMBER :=& percent ;

v_dno NUMBER :=& dno ;

BEGIN

sql_stat := 'UPDATE tb2 SET sal = sal * (1 + :percent / 100)' -- 动态 DML 语句

|| ' WHERE deptno = :dno'

|| ' RETURNING ename, sal INTO :name, :salary' ; -- 使用了 RETURNING 子句,有返回值

EXECUTE IMMEDIATE sql_stat USING v_percent , v_dno -- 执行动态 SQL 语句

RETURNING BULK COLLECT INTO ename_table , sal_table ; -- 使用 BULK COLLECT INTO 到集合变量

FOR i IN 1. . ename_table . COUNT -- 使用 FOR 循环读取集合变量的结果

DBMS_OUTPUT . PUT_LINE ( 'Employee ' || ename_table ( i ) || ' Salary is: ' || sal_table ( i ));

END LOOP ;

END ;

Employee Henry Salary is: 1694

Employee JONES Salary is: 3841.75

Employee ADAMS Salary is: 1573

Employee FORD Salary is: 3872

3. 使用 EXECUTE IMMEDIATE 结合 BULK 子句处理多行查询

下面示例中,与前一个示例相同,只不过其动态 SQL 有查询语句组成,且返回多个结果集,同样使用了 BULK COLLECT INTO 来传递结果。

DECLARE

TYPE ename_table_type IS TABLE OF tb2 . ename % TYPE INDEX BY BINARY_INTEGER ; -- 定义类型用于存放结果集

TYPE sal_table_type IS TABLE OF tb2 . sal % TYPE INDEX BY BINARY_INTEGER ;

ename_table ename_table_type ;

sal_table sal_table_type ;

sql_stat VARCHAR2 ( 100 );

BEGIN

sql_stat := 'SELECT ename,sal FROM tb2 WHERE deptno = :dno' ; -- 动态 DQL 语句,未使用 RETURNING 子句

EXECUTE IMMEDIATE sql_stat BULK COLLECT INTO ename_table , sal_table USING & dno ; -- 使用 BULK COLLECT INTO

FOR i IN 1. . ename_table . COUNT

DBMS_OUTPUT . PUT_LINE ( 'Employee ' || ename_table ( i ) || ' Salary is: ' || sal_table ( i ));

END LOOP ;

END ;

Employee Henry Salary is: 1694

Employee JONES Salary is: 3841.75

Employee ADAMS Salary is: 1573

Employee FORD Salary is: 4259.2

4. 使用 FETCH 子句结合 BULK 子句处理多行结果集

下面的示例中首先定义了游标类型,游标变量以及复合类型,复合变量,接下来从动态 SQL OPEN 游标,然后使用 FETCH 将结果存放到复

合变量中。即使用 OPEN FETCH 代替了 EXECUTE IMMEDIATE 来完成动态 SQL 的执行。

DECLARE

TYPE empcurtype IS REF CURSOR ; -- 定义游标类型及游标变量

emp_cv empcurtype ;

TYPE ename_table_type IS TABLE OF tb2 . ename % TYPE INDEX BY BINARY_INTEGER ; -- 定义结果集类型及变量

ename_table ename_table_type ;

sql_stat VARCHAR2 ( 120 );

BEGIN

sql_stat := 'SELECT ename FROM tb2 WHERE deptno = :dno' ; -- 动态 SQL 字符串

OPEN emp_cv FOR sql_stat -- 从动态 SQL 中打开游标

USING & dno ;

FETCH emp_cv BULK COLLECT -- 使用 BULK COLLECT INTO 提取结果集

INTO ename_table ;

FOR i IN 1 .. ename_table . COUNT LOOP

DBMS_OUTPUT . PUT_LINE ( 'Employee Name is ' || ename_table ( i ));

END LOOP ;

CLOSE emp_cv ;

END ;

Employee Name is Henry

Employee Name is JONES

Employee Name is ADAMS

Employee Name is FORD

5. 使用 FORALL 语句中使用 BULK 子句

下面是 FORALL 子句的语法

FORALL index IN lower bound..upper bound --FORALL 循环计数

EXECUTE IMMEDIATE dynamic_string -- 结合 EXECUTE IMMEDIATE 来执行动态 SQL 语句

USING bind_argument | bind_argument(index) -- 绑定输入参数

[bind_argument | bind_argument(index)] ...

[{RETURNING | RETURN} BULK COLLECT INTO bind_argument[,bind_argument...]]; -- 绑定返回结果集

FORALL 子句允许为动态 SQL 输入变量,但 FORALL 子句仅支持 DML(INSERT,DELETE,UPDATE) 语句,不支持动态的 SELECT 语句。

下面的示例中,首先声明了两个复合类型以及复合变量,接下来为复合变量 ename_table 赋值,以形成动态 SQL 语句。紧接着使用 FORALL

子句结合 EXECUTE IMMEDIATE 来提取结果集。

DECLARE -- 定义复合类型及变量

TYPE ename_table_type IS TABLE OF tb2 . ename % TYPE ;

TYPE sal_table_type IS TABLE OF tb2 . sal % TYPE ;

ename_table ename_table_type ;

sal_table sal_table_type ;

sql_stat VARCHAR2 ( 100 );

BEGIN

ename_table := ename_table_type ( 'BLAKE' , 'FORD' , 'MILLER' ); -- 为复合类型赋值

sql_stat := 'UPDATE tb2 SET sal = sal * 1.1 WHERE ename = :1' -- 定义动态 SQL 语句

|| ' RETURNING sal INTO :2' ;

FORALL i IN 1. . ename_table . COUNT -- FORALL 设定起始值

EXECUTE IMMEDIATE sql_stat USING ename_table ( i ) -- 使用 EXECUTE IMMEDIATE 结合 RETURNING BULK COLLECT INTO 获取结果集

RETURNING BULK COLLECT INTO sal_table ;

FOR j IN 1. . ename_table . COUNT

DBMS_OUTPUT . PUT_LINE ( 'The new salary is ' || sal_table ( j ) || ' for ' || ename_table ( j )) ;

END LOOP ;

END ;

The new salary is 3135 for BLAKE

The new salary is 4259.2 for FORD

The new salary is 1760 for MILLER

6. 动态 SQL 使用的常见错误,请参考: PL/SQL --> 动态 SQL 的常见错误

六、更多参考

有关 SQL 请参考

SQL 基础--> 子查询

SQL 基础--> 多表 查询

SQL 基础--> 分组与分组函数

SQL 基础--> 常用函数

SQL 基础--> ROLLUP 与CUBE 运算符实现数据汇总

SQL 基础--> 层次化查询(START BY ... CONNECT BY PRIOR)

有关 PL/SQL 请参考

PL/SQL --> 语言基础

PL/SQL --> 流程控制

PL/SQL --> 存储过程

PL/SQL --> 函数

PL/SQL --> 游标

PL/SQL --> 隐式游标(SQL%FOUND)

PL/SQL --> 异常处理(Exception)

PL/SQL --> PL/SQL

PL/SQL --> 包的创建与管理

PL/SQL --> 包重载、初始化

PL/SQL --> DBMS_DDL 包的使用

PL/SQL --> DML 触发器

PL/SQL --> INSTEAD OF 触发器

使用动态SQL是在编写PL/SQL过程时经常使用的方法之一。很多情况下,比如根据业务的需要,如果输入不同查询条件,则生成不同的执行SQL查询语句,对于这种情况需要使用动态SQL来完成。再比如,对于分页的情况,对于不同的表……
本章主要讲解了 动态SQL 相关知识。首先讲解了 动态SQL 的元素;其次讲解了条件 询操作,包括元素、元素、元素、元素、元素和元素的使用;然后讲解了更新操作;最后讲解了复杂 询操作。通过本章的学习,读者可以了解常用 动态SQL 元素的主要作用,并能够掌握这些元素在实际开发 的应用。在MyBatis框架 ,这些 动态SQL 元素十分重要,熟练的掌握它们能够极大地提高开发效率。 .....................
1、什么是 PL / SQL ? PLSQL 是Oracle对 sql 语言的过程化扩展 (类似于Basic) 指在 SQL 命令语言 增加了过程处理语句(如分支、循环等),使 SQL 语言具有过程处理能力。(减少数据库和服务器之间的交互,提高执行效率) 2、程序结构 PLSQL 语言的大小写是不区分的, PL / SQL 可以分为三个部分:声明部分、可执行部分、异常处理部分。 DECLARE – 声明 变量 、游标。 I INTEGER ; BEGIN – 执行语句 –[异常处理] 其 DECLARE部分用来声明 变量 或游标(结果集
PL / SQL 程序开发 ,可以使用DML语句和事务控制语句,但是还有很多语句(比如DDL语句)不能直接在 PL / SQL 执行。 这些语句可以使用 动态SQL 实现 。 语法格式: 动态SQL EXECUTE IMMEDIATE 动态 语句字符串 [INTO 变量 ] [USING 参数列 ] BEGIN EXECUTE IMMEDIATE 'CREATE TABLE TMP_BAK AS SELECT * FROM TMP'; -- 字符串语句最后不要加分号; DECLARE
这个错误提示通常是在使用 PL / SQL 语言时遇到的, PL / SQL 解释器无法识别或处理 SQL 语句。这可能是因为语法有误、语句缺失、拼写错误等问题导致的。 要解决这个问题,可以考虑以下步骤: 1. 仔细检 SQL 语句的语法和拼写,确保其正确无误。 2. 确保 SQL 语句所需的 、字段、视图等已经存在,并且有权限访问它们。 3. 检 PL / SQL 代码 是否存在其他语法或逻辑错误,如缺少分号、未 定义 变量 等。 4. 可以尝试将 SQL 语句单独提取出来,使用 SQL 客户端工具进行测试,以确定是否能够正确执行。 如果以上步骤无法解决问题,可以尝试在 PL / SQL 代码 加入调试语句或日志,以更深入地了解代码的执行过程和出现错误的原因。