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

系统 1722 0

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

-- 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)

 

    有关 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 触发器

PL/SQL --> 动态 SQL

 

 

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


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论