`
sangei
  • 浏览: 337275 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

(转)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_enamev_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.动态SQLRETURNING INTO返回DML操作的结果,对于SELECT查询返回的结果,在执行EXECUTE IMMEDIATE时,直接使用INTO子句来传递。

分享到:
评论

相关推荐

    PL/SQL 基本知识

    在实际应用中,PL/SQL的存储过程和函数是常见的使用场景。存储过程是一组预编译的PL/SQL语句,可以被多次调用,减少了网络传输的开销,提高了性能。函数则返回一个值,常用于计算或数据转换。例如,在`jbpm.sql`文件...

    pl/sql64位

    1. **代码编辑器**:提供语法高亮、自动完成和错误检查,使得编写PL/SQL代码更加便捷。 2. **数据库连接**:支持多种Oracle数据库版本的连接,无需复杂的配置步骤,只需输入必要的连接信息。 3. **对象浏览器**:...

    PL/SQL下载

    PL/SQL是Oracle数据库系统中的一个关键组成部分,它是一种结合了SQL语言与过程编程的语言,专为数据库管理和应用程序开发设计。PL/SQL全称为“Procedural Language/Structured Query Language”,它提供了丰富的控制...

    PL/SQL免安装版

    PL/SQL是Oracle数据库系统中的一个关键组件,它是一种结合了SQL语言与过程编程的语言,专为数据库管理和开发设计。PL代表Procedural Language(过程语言),SQL则是Structured Query Language(结构化查询语言)。...

    PL/SQL Developer 7.0

    1. **代码编辑器**:PL/SQL Developer提供了智能的代码编辑器,具备语法高亮、自动完成、错误检查等功能,使编写PL/SQL代码变得更加轻松快捷。 2. **调试器**:内置的调试工具允许开发者设置断点,单步执行代码,...

    pl/sql developer 用户指南中文版

    PL/SQL Developer是一款专为Oracle数据库设计的强大集成开发环境(IDE),主要用于编写、调试和管理PL/SQL代码。用户指南中文版是针对中国用户提供的详细教程,帮助他们更好地理解和使用这款工具。以下是对PL/SQL ...

    PL/SQL Developer 中文版帮助手册(PDF)

    根据提供的信息来看,这份文档似乎是一份关于PL/SQL Developer的中文版帮助手册。由于实际内容部分呈现为乱码,无法直接解读具体内容,因此这里将根据标题、描述以及标签来进行有关PL/SQL Developer的一些基本知识点...

    PL/SQL工具

    PL/SQL工具是专为Oracle数据库设计的一种强大的前端操作软件,它极大地简化了数据库管理员和开发人员对数据库的管理和开发工作。在Oracle数据库系统中,PL/SQL是一种过程化语言,结合了SQL(结构化查询语言)的查询...

    ORACLE PL/SQL804+中文补丁

    "ORACLE PL/SQL 804+中文补丁"是针对Oracle数据库管理系统的一个特定版本,804可能指的是8.0.4版本,这在20世纪90年代末期是一个常见的版本。这个补丁主要是为了增强PL/SQL在中文环境下的支持,解决可能存在的字符集...

    pl/sql开发工具

    PL/SQL Developer是一款专为PL/SQL编程和Oracle数据库管理设计的强大开发工具。这款软件提供了全面的功能,使得数据库开发者和管理员能够高效地进行各种数据库操作,包括编写、调试、执行和管理PL/SQL代码,以及...

    pl/sql developer 中文指南

    PL/SQL Developer的调试器是其一大亮点,它支持设置断点、单步执行、查看变量值、调用堆栈等调试功能,帮助开发者追踪和定位程序中的错误。 5. **数据查看与操作** 工具内置了数据浏览和操作功能,可以直接在结果...

    PL/SQL中文帮助手册

    这个7.0版的"PL/SQL中文帮助手册"很可能详细介绍了以上这些内容,并且可能还包含了更深入的编程技巧、最佳实践以及常见问题的解决方案。通过阅读和学习这本手册,开发者可以有效地提升在Oracle数据库环境下使用PL/...

    PL/SQL Developer快速输入插件

    这个插件允许用户通过简短的键盘输入触发预定义的代码块,从而快速生成常见的SQL和PL/SQL语句,节省了大量手动键入的时间。 标题中的"PL/SQL Developer快速输入插件"指的是这个能够提升编程速度的实用工具。例如,...

    Oracle PL/SQL by Example(4th Edition)

    10. **错误处理和调试**:学习如何识别和解决PL/SQL中的错误,以及如何使用调试工具进行问题定位。 通过本书,读者不仅能够获得丰富的理论知识,还能通过大量的实例和练习来加深对PL/SQL的理解。无论你是初学者还是...

    PL/SQL7.5开发工具

    1. **源代码编辑**:提供代码自动完成、语法高亮、错误检查等功能,帮助开发者快速编写高质量的PL/SQL代码。 2. **调试功能**:支持断点设置、单步执行、查看变量值、调用堆栈跟踪等,使得调试过程更为直观和便捷。 ...

    plsql developer 11 中文手册.pdf

    工作站安装是最常见的安装方式,用户可以直接在自己的计算机上安装 PL/SQL Developer 11。 2.3 基于服务器的安装 基于服务器的安装方式适合大型企业或团队,服务器可以提供集中化的管理和维护。 2.4 脚本安装 ...

    ORACLE8 PLSQL程序设计_sql_code.zip_oracle pl/sql_pl sql code_pl/sql_

    4. **CV1**: "CV1"可能代表"Cursor Variable 1",04-CV1.PC可能涉及到如何声明和使用游标变量,这是PL/SQL中处理查询结果集的常见方法。 5. **PRCMP**: "PRCMP"可能指的是过程或函数的编译,07-PRCMP.PC可能涵盖...

    PL/SQL安装文件(7.0.0.1050版本)

    3. **PL/SQL调试器**:内置的调试器是PL/SQL Developer的一大亮点,它允许用户设置断点、查看变量值、跟踪调用堆栈,帮助找出代码中的错误。 4. **报表和图表生成器**:这些工具可以帮助用户生成关于数据库对象和...

    PL/SQL Developer V7.1.5汉化包

    PL/SQL Developer是一款专为Oracle数据库设计的集成开发环境(IDE),它由Allround Automations公司开发,主要用于编写、调试、测试和管理PL/SQL代码。V7.1.5是该软件的一个版本,提供了对PL/SQL语言的强大支持,...

Global site tag (gtag.js) - Google Analytics