PL/SQL --> 动态SQL的常见错误

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

-- PL/SQL --> 动态SQL的常见错误

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

动态SQL在使用时,有很多需要注意的地方,如动态SQL语句结尾处不能使用分号(;),而动态PL/SQL结尾处需要使用分号(;),但不能使用正

斜杠结尾(/),以及shcema对象不能直接作为变量绑定。本文介绍了动态SQL的常见问题。

一、演示动态SQL的使用

下面的示例中,首先使用动态SQL基于scott.emp创建表tb2,然后里直接使用动态SQL从新表中获取记录数并输出。再接下来是定义了一个动

态PL/SQL代码并执行以获取当前的系统时间,最后使用动态SQL对新表进行更新。

DECLARE --定义变量以及给变量设定初始值

sql_stmt VARCHAR2(100);

plsql_block VARCHAR2(300);

v_deptno NUMBER := 30;

v_count NUMBER;

v_new_sal VARCHAR2(5);

v_empno NUMBER := 7900;

BEGIN

sql_stmt := 'CREATE TABLE tb_emp ' || --为变量赋值,生成动态SQL语句

'AS SELECT * FROM scott.emp WHERE deptno = ' || v_deptno;

EXECUTE IMMEDIATE sql_stmt; --执行动态SQL语句

EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM tb_emp' --直接使用EXECUTE IMMEDIATE后跟动态SQL串获得新表的记录数

INTO v_count;

DBMS_OUTPUT.PUT_LINE('The employee count is : ' || v_count);

plsql_block := 'DECLARE ' || --声明一个PL/SQL块,存放到变量plsql_block中

' v_date DATE; ' ||

'BEGIN ' ||

' SELECT SYSDATE INTO v_date FROM DUAL; ' ||

' DBMS_OUTPUT.PUT_LINE (TO_CHAR(v_date,''DD-MON-YYYY''));' ||

'END;';

EXECUTE IMMEDIATE plsql_block; --执行动态的PL/SQL块

sql_stmt := 'UPDATE tb_emp SET sal = sal + 100 WHERE empno =:eno ' || --更新新表的一条记录

'RETURNING sal INTO :sal'; --动态SQL语句中包含RETURNING子句返回更新后的结果

EXECUTE IMMEDIATE sql_stmt --执行动态SQL块

USING v_empno

RETURNING INTO v_new_sal; --使用RETURNING子句将结果存放到变量v_new_sal中

DBMS_OUTPUT.PUT_LINE('New salary is: ' || v_new_sal);

END;

The employee count is : 6

04-JAN-2011

New salary is: 1050

二、动态SQL的常见错误

1.使用动态DDL时,不能使用绑定变量

下面的示例中,在创建表示,使用了绑定变量:dno,在执行的时候收到了错误信息。

DECLARE

sql_stmt VARCHAR2(100);

v_deptno VARCHAR2(5) := '30';

v_count NUMBER;

BEGIN

sql_stmt := 'CREATE TABLE tb_tmp ' || 'AS SELECT * FROM scott.emp ' ||

'WHERE deptno = :dno';

EXECUTE IMMEDIATE sql_stmt

USING v_deptno;

EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM tb_tmp'

INTO v_count;

DBMS_OUTPUT.PUT_LINE('The temp table count is ' || v_count);

END;

DECLARE

*

ERROR at line 1:

ORA-01027: bind variables not allowed for data definition operations

ORA-06512: at line 8

解决办法,将绑定变量直接拼接,如下:

sql_stmt := 'CREATE TABLE tb_tmp ' || 'AS SELECT * FROM scott.emp ' || 'WHERE deptno = ' || v_deptno;

2.不能使用schema对象作为绑定参数

下面的示例中,动态SQL语句查询需要传递表名,因此收到了错误提示。

DECLARE

sql_stmt VARCHAR2(100);

v_count NUMBER;

BEGIN

EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM :tb_name'

INTO v_count;

DBMS_OUTPUT.PUT_LINE('The table record is ' || v_count);

END;

DECLARE

*

ERROR at line 1:

ORA-00903: invalid table name

ORA-06512: at line 5

处理办法

DECLARE

sql_stmt VARCHAR2(100);

v_tablename VARCHAR2(30) :='scott.emp'; --增加一个变量并赋值

v_count NUMBER;

BEGIN

EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM ' || v_tablename --使用|| 连接变量

INTO v_count;

DBMS_OUTPUT.PUT_LINE('The table record is ' || v_count);

END;

The temp table count is 14

3.动态SQL块不能使用分号结束(;)

下面的示例中,动态SQL语句使用了分号来结束,收到错误提示。

DECLARE

sql_stmt VARCHAR2(100);

--v_tablename VARCHAR2(30) :='scott.emp';

v_count NUMBER;

BEGIN

EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM tb_emp;' --此处多出了分号,应该去掉

INTO v_count;

DBMS_OUTPUT.PUT_LINE('The temp table count is ' || v_count);

END;

DECLARE

*

ERROR at line 1:

ORA-00911: invalid character

ORA-06512: at line 6

处理办法

去掉动态SQL语句末尾的分号

4.动态PL/SQL块不能使用正斜杠来结束块,但是块结尾处必须要使用分号(;)

DECLARE

plsql_block VARCHAR2(300);

BEGIN

plsql_block := 'DECLARE ' ||

' v_date DATE; ' ||

' BEGIN ' ||

' SELECT SYSDATE INTO v_date FROM DUAL; ' ||

' DBMS_OUTPUT.PUT_LINE (TO_CHAR(v_date,''YYYY-MM-DD''));' ||

'END;

/'; --此处多出了/,应该将其去掉

EXECUTE IMMEDIATE plsql_block;

END;

DECLARE

*

ERROR at line 1:

ORA-06550: line 3, column 2:

PLS-00103: Encountered the symbol "/" The symbol "/" was ignored.

ORA-06512: at line 13

处理办法

去掉动态PL/SQL 块尾部的斜杠

5.空值传递的问题

下面的示例中对表tb_emp更新,并将空值更新到sal列,直接使用USING NULL收到错误提示。

DECLARE

sql_stmt VARCHAR2(100);

v_empno NUMBER := 7900;

BEGIN

sql_stmt := 'UPDATE tb_emp SET sal = :new_sal WHERE empno=:eno';

EXECUTE IMMEDIATE sql_stmt

USING NULL,v_empno; --此处不能直接使用NULL

END;

USING NULL,v_empno;

*

ERROR at line 7:

ORA-06550: line 7, column 11:

PLS-00457: expressions have to be of SQL types

ORA-06550: line 6, column 3:

PL/SQL: Statement ignored

正确的处理办法

DECLARE

sql_stmt VARCHAR2(100);

v_empno NUMBER := 7900;

v_sal NUMBER; --声明一个新变量,但不赋值

BEGIN

sql_stmt := 'UPDATE tb_emp SET sal = :new_sal WHERE empno=:eno';

EXECUTE IMMEDIATE sql_stmt

USING v_sal, v_empno;

COMMIT;

DBMS_OUTPUT.PUT_LINE('The new sal is NULL');

END;

6.传递参数时顺序不正确的问题

使用USING传递参数到动态SQL或使用INTO子句传递结果集到变量应注意按正确的顺序排列处理

下面的示例中由于v_ename与v_sal为不同的数据类型,在使用INTO时不小心将顺序颠倒,导致错误产生。当然,如果数据类型相同,

且不会存在溢出的情况下将没有错误提示。

DECLARE

TYPE emp_cur_type IS REF CURSOR;

emp_cv emp_cur_type;

sql_stat VARCHAR2(100);

v_dno NUMBER := &dno;

v_ename VARCHAR2(25);

v_sal NUMBER;

BEGIN

sql_stat := 'SELECT ename,sal FROM scott.emp WHERE deptno = :dno';

OPEN emp_cv FOR sql_stat --使用游标来处理动态SQL

USING v_dno;

LOOP

FETCH emp_cv

INTO v_sal, v_ename; --从结果集中提取记录时,顺序发生颠倒

EXIT WHEN emp_cv%NOTFOUND;

dbms_output.put_line('Employee name is :' || v_ename ||

', The sal is :' || v_sal);

END LOOP;

CLOSE emp_cv;

END;

Enter value for dno: 20

old 5: v_dno NUMBER := &dno;

new 5: v_dno NUMBER := 20;

DECLARE

*

ERROR at line 1:

ORA-01722: invalid number

ORA-06512: at line 14

处理办法

更正参数变量的顺序

7.日期和字符型必须要使用引号来处理

下面的示例中,使用了日期型变量,未使用引号标注,且使用了变量绑定,但直接输入日期型数据,而不加引号,则收到错误提示。

DECLARE

sql_stat VARCHAR2(100);

v_date DATE :=&dt; --定义日期型变量,未使用引号

v_empno NUMBER :=7900;

v_ename tb_emp.ename%TYPE;

v_sal tb_emp.sal%TYPE;

BEGIN

sql_stat := 'SELECT ename,sal FROM tb_emp WHERE hiredate=:v_date'; --使用了占位符:v_date进行变量绑定

EXECUTE IMMEDIATE sql_stat

INTO v_ename,v_sal

USING v_date;

DBMS_OUTPUT.PUT_LINE('Employee Name '||v_ename||', sal is '||v_sal);

END;

Enter value for dt: 1981-05-01 --执行时,输入的字串中也未使用引号,此时收到错误提示

old 3: v_date DATE :=&dt;

new 3: v_date DATE :=1981-05-01;

v_date DATE :=1981-05-01;

*

ERROR at line 3:

ORA-06550: line 3, column 20:

PLS-00382: expression is of wrong type

ORA-06550: line 3, column 13:

PL/SQL: Item ignored

ORA-06550: line 13, column 9:

PLS-00320: the declaration of the type of this expression is incomplete or malformed

ORA-06550: line 11, column 3:

PL/SQL: Statement ignored

处理办法一

执行时输入带引号的字串

flasher@ORCL> /

Enter value for dt: '1981-05-01'

old 3: v_date DATE :=&dt;

new 3: v_date DATE :='1981-05-01';

Employee Name BLAKE, sal is 2850

PL/SQL procedure successfully completed.

处理办法二

在声明变量时赋值用引号,如下

v_date DATE :='&dt';

如存在字符格式转换,可以直接使用转换函数,如

v_date DATE :=TO_DATE('&dt','DD-MON-RR');

如果上面的例子中,动态SQL语句不使用绑定日期变量,而是将其连接成字符串,则可以使用下面的方式来实现

DECLARE

sql_stat VARCHAR2(100);

v_date DATE :='&dt';

v_empno NUMBER :=7900;

v_ename tb_emp.ename%TYPE;

v_sal tb_emp.sal%TYPE;

BEGIN

sql_stat := 'SELECT ename,sal FROM tb_emp WHERE hiredate=' || chr(39) ||v_date|| chr(39);--chr(39)代表单引号

EXECUTE IMMEDIATE sql_stat

INTO v_ename,v_sal;

DBMS_OUTPUT.PUT_LINE('Employee Name '||v_ename||', sal is '||v_sal);

END;

Enter value for dt: 1981-05-01

old 3: v_date DATE :='&dt';

new 3: v_date DATE :='1981-05-01';

SELECT ename,sal FROM tb_emp WHERE hiredate='1981-05-01'

Employee Name BLAKE, sal is 2850

PL/SQL procedure successfully completed.

8.单行SELECT 查询不能使用RETURNING INTO返回

下面的示例中,使用了动态的单行SELECT查询,并且使用了RETURNING子句来返回值。事实上,RETURNING coloumn_name INTO 子句仅

仅支持对DML结果集的返回,因此,收到了错误提示。

DECLARE

sql_stat VARCHAR2(200);

v_empno tb2.empno%TYPE := &eno;

v_ename tb2.ename%TYPE;

BEGIN

sql_stat := 'SELECT ename FROM tb2 WHERE empno =:eno

RETURNING ename INTO :v_ename ';

EXECUTE IMMEDIATE sql_stat

USING v_empno

RETURNING INTO v_ename;

DBMS_OUTPUT.PUT_LINE('The employee name is ' || v_ename);

END;

处理办法

去掉动态SQL语句中的RETURNING coloumn_name INTO子句,在执行EXECUTE IMMEDIATE时,直接使用INTO子句来传递值。

DECLARE

sql_stat VARCHAR2(200);

v_empno tb2.empno%TYPE := &eno;

v_ename tb2.ename%TYPE;

BEGIN

sql_stat := 'SELECT ename FROM tb2 WHERE empno =:eno';

-- RETURNING ename INTO :v_ename '; --去掉RETURNING子句

EXECUTE IMMEDIATE sql_stat

INTO v_ename --增加INTO子句来返回变量值

USING v_empno;

--RETURNING INTO v_ename; --去掉RETURNING子句

DBMS_OUTPUT.PUT_LINE('The employee name is ' || v_ename);

END;

三、总结

1.使用动态DDL时,不能使用绑定变量。应该将绑定变量与原动态SQL使用连接符进行连接。

2.不能使用schema对象作为绑定参数,将schema对象与原动态SQL使用连接符进行连接。

3.动态SQL块不能使用分号结束(;)。

4.动态PL/SQL块不能使用正斜杠来结束块,但是块结尾处必须要使用分号(;)。

5.空值传递的时候,不能直接使用USING NULL子句,应当声明变量,使用变量传递,当未给变量赋值时,即为空值。

6.参数的传入传出应保证顺序的正确,以及防止数据溢出的问题。

7.日期型或字符型在动态SQL中处理时,需要注意单引号个数的问题,特殊情况下可以使用chr(39)作为单引号使用。

8.动态SQL中RETURNING INTO返回DML操作的结果,对于SELECT查询返回的结果,在执行EXECUTE IMMEDIATE时,直接使用INTO子句来传递。

四、更多参考

有关SQL请参考

SQL 基础--> 子查询

SQL 基础-->多表查询

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

SQL 基础-->常用函数

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

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏个人随笔

MySQL 关于索引那点事

索引 其实数据库中的数据是按页存放的 其实索引也是按页存放的 所以本质上索引也占硬盘空间(以最小的消耗,换取最大的利益) 索引是一种有效组合数据的方式!为快速...

29390
来自专栏学习有记

T-SQL进阶:超越基础 Level 2:编写子查询

11210
来自专栏CaiRui

Mysql-5-数据表的基本操作

1.创建表:之前需要use database database_name 然后create table 表名(); 例:创建员工表tb_employee1,结构...

24960
来自专栏资深Tester

增删改查的增删改

24840
来自专栏mukekeheart的iOS之旅

MySQL学习笔记(二)

二、SQL基本知识 SQL 是一种典型的非过程化程序设计语言,这种语言的特点是:只指定哪些数据被操纵,至于对这些数据要执行哪些操作,以及这些操作是如何执行的,则...

280100
来自专栏乐沙弥的世界

PL/SQL --> 动态SQL

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

7910
来自专栏乐沙弥的世界

ORA-06502 assigning values from SQL to PL/SQL variables

    最近SQL查询返回的结果给PL/SQL变量出现ORA-06502错误。这个错误的描述是ORA-06502: PL/SQL: numeric or val...

7210
来自专栏Linux运维学习之路

MySQL索引

索引管理 索引是什么? 索引就好比一本书的目录,它会让你更快的找到内容; 让获取的数据更有目的性,从而提高数据库检索数据的性能; 索引建立在表的列上(字段)。 ...

47750
来自专栏乐沙弥的世界

PL/SQL --> 存储过程

存储过程子程序的一种类型,能够完成一些任务,作为schema对象存储于数据库。是一个有名字的PL/SQL代码块,支持接收或不接受参数

9030
来自专栏程序员宝库

再见乱码:5 分钟读懂 MySQL 字符集设置

作者: 程序猿小卡_casper 原文:https://segmentfault.com/a/1190000012775484 一、内容概述 在MySQL的使...

511110

扫码关注云+社区

领取腾讯云代金券