文辉居士

PL/SQL动态SQL

概述

使用动态SQL是在编写PL/SQL过程时经常使用的方法之一。很多情况下,比如根据业务的需要,如果输入不同查询条件,则生成不同的执行SQL查询语句,对于这种情况需要使用动态SQL来完成。再比如,对于分页的情况,对于不同的表,必定存在不同的字段,因此使用静态SQL则只能针对某几个特定的表来形成分页。而使用动态的SQL,则可以对不同的表,不同的字段进行不同的分页。这些情况的处理通常都是用动态SQL来完成。

动态SQL和静态SQL
静态SQL
静态SQL通常用于完成可以确定的任务。比如传递部门号调用存储过程,返回该部门的所有雇员及薪水信息,则该语句为
SELECT ename,sal INTO lv_ename,lv_sal FROM scott.emp WHERE deptno=&dno;
对于上述类似的DML语句在第一次运行时进行编译,而后续再次调用,则不再编译该过程。即一次编译,多次调用,使用的相同的执行计划。此种方式被称之为使用的是静态的SQL。
动态SQL
动态SQL通常是用来根据不同的需求完成不同的任务。比如分页查询,对于表emp分页,需要使用字段雇员姓名,薪水,雇用日期,且按薪水降序生成报表,每页显示行数据。而对于表sales,需要使用字段雇员名称,客户名称,销售数量,销售日期,且按销售日期升序排列。以上两种情况,可以创建存储过程来对其进行分页,通过定义变量,根据输入不同的表名,字段名,排序方法来生成不同的SQL语句。对于输入不同的参数,SQL在每次运行时需要事先对其编译。即多次调用则需要多次编译,此称之为动态SQL。动态SQL语句通常存放在字符串变量中,且SQL语句可以包含占位符(使用冒号开头)。也可以直接将动态SQL紧跟在EXECUTE IMMEDIATE语句之后,如

EXECUTE IMMEDIATE 'alter table emp enable row movement'

两者的异同
静态SQL为直接嵌入到PL/SQL中的代码,而动态SQL在运行时,根据不同的情况产生不同的SQL语句。
静态SQL为在执行前编译,一次编译,多次运行。动态SQL同样在执行前编译,但每次执行需要重新编译。
静态SQL可以使用相同的执行计划,对于确定的任务而言,静态SQL更具有高效性。但缺乏灵活性动态SQL使用了不同的执行计划,效率不如静态SQL,但能够解决复杂的问题。
动态SQL容易产生SQL注入,为数据库安全带来隐患。
动态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, ...]
语法描述
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动态返回值的变量。
使用要点
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以及单行结果集)

例:使用EXECUTE IMMEDIATE处理DDL操作。在存储过程之中封装一简单的DDL语句,通过传入表名来进行调用。     
SQL> create table tb2 as select * from emp;
Table created.
SQL> create or replace procedure trunc_table(table_name varchar2)
  2  as
  3  sql_statement varchar2(100);
  4  begin
  5    sql_statement := 'TRUNCATE TABLE ' || table_name;     --为变量进行赋值,用于生成动态SQL语句
  6    execute immediate sql_statement;    --使用EXECUTE IMMEDIATE执行动态SQL语句
  7  end;
  8  /
Procedure created.
SQL> select count(1) from tb2;
  COUNT(1)
----------
        14
SQL> exec trunc_table('tb2');
PL/SQL procedure successfully completed.
SQL> select count(1) from tb2;
  COUNT(1)
----------
         0
例:使用EXECUTE IMMEDIATE处理DCL操作,下面使用sys帐户创建存储过程grant_sys_priv用于给用户授予权限
SQL> conn sys/redhat@orcl as sysdba
SQL>CREATE OR REPLACE PROCEDURE grant_sys_priv(priv VARCHAR2, username VARCHAR2)
IS
    sql_stat VARCHAR2(100);
BEGIN
    sql_stat := 'GRANT ' || priv || ' TO ' || username; 
    EXECUTE IMMEDIATE sql_stat;
END;
/  
SQL> exec grant_sys_priv('connect','usr1');
例:使用EXECUTE IMMEDIATE处理没有参数传入传出的DML语句
SQL> select * From tb2 where empno = 7900;
     EMPNO ENAME           SAL
----------      -----------       ------------
      7900    JAMES           950
SQL> declare
  2    sql_stat varchar2(100);
  3  begin
  4    sql_stat := 'DELETE FROM scott.tb2 where empno = 7900';
  5    execute immediate sql_stat;
  6  end;
  7  /
PL/SQL procedure successfully completed.
SQL> select * From tb2 where empno = 7900;
no rows selected

例:有参数传入的DML语句(使用USING子句),对于使用了参数传入的动态SQL,需要使用USING子句来指明传入的参数。在下面的示例中,为表tb2插入一条记录,在DML语句中使用了四个占位符(占位符用以冒号开头,紧跟任意字母或数字表示)。因此在使用EXECUTE IMMEDIATE使用USING子句为其指定其参数。
SQL>   declare
  2    sql_stat varchar2(100);
  3    lv_empno tb2.empno%type :=7900;
  4    lv_ename tb2.ename%type :='JAMES';
  5    lv_sal   tb2.sal%type :=950;
  6  begin 
  7    sql_stat := 'INSERT INTO tb2 values(:1,:2,:3)';   --DML语句中使用了占位符
  8    execute immediate sql_stat using lv_empno,lv_ename,lv_sal;      --为占位符指定参数或值
  9    commit;
 10* end;
PL/SQL procedure successfully completed.

SQL> select * from tb2 where empno=7900;
  EMPNO   ENAME           SAL
----------    ----------------  -------------
      7900   JAMES             950

例:处理包含returning子句的DML语句。下面的示例中,对表tb2进行更新,使用了两个占位符,一个是:percent,一个是:eno,因此在使用EXECUTE IMMEDIATE执行动态。DML时,需要使用USING子句且带两个输入参数。其次,动态DML中使用了RETURNING sal INTO :salary,因此EXECUTE IMMEDIATE后也必须使用RETURNING INTO varialbe_name。

SQL>  declare
  2     sql_stat varchar2(100);
  3     v_sal tb2.sal%type;
  4   begin
  5     sql_stat :='update tb2 set sal = sal * (1 + :percent/100)'
  6     || ' where empno = :eno returning sal into :v_sal';    --使用了占位符:eno,:salary,以及RETURNING子句
  7     execute immediate sql_stat using &1,&2 returning into v_sal;   --必须使用USING及RETURNING子句
  8     commit;
  9     dbms_output.put_line('New salary: ' || v_sal);
 10   end;
 11  /
Enter value for 1: 10
Enter value for 2: 7900
old   7:    execute immediate sql_stat using &1,&2 returning into v_sal;
new   7:    execute immediate sql_stat using 10,7900 returning into v_sal;
New salary: 1045
PL/SQL procedure successfully completed.

例:处理包含检索值的单行查询。下面的示例中,使用SELECT 查询获得单行结果集,使用了占位符:name,因此也需要使用USING子句为其传递参数
SQL> declare
  2    sql_stat varchar2(100);
  3    emp_record tb2%rowtype;
  4  begin
  5    sql_stat := 'select * from tb2 where ename = upper(:name)';
  6    execute immediate sql_stat into emp_record using '&name';
  7    DBMS_OUTPUT.PUT_LINE('The salary is ' || emp_record.sal || ' for ' || emp_record.ename);
  8  end;
  9  /
Enter value for name: james
old   6:   execute immediate sql_stat into emp_record using '&name';
new   6:   execute immediate sql_stat into emp_record using 'james';
The salary is 1045 for JAMES
PL/SQL procedure successfully completed.

游标和动态SQL处理多行结果集的查询语句

其主要流程为

1.定义游标变量
TYPE cursortype IS REF CURSOR;
cursor_variable cursortype;
2.打开游标变量
OPEN cursor_variable FOR dynamic_string
[USING bind_argument[,bind_argument]...]
3.循环提取数据
FETCH cursor_variable INTO {var1[,var2]...| record_variable};
EXIT WHEN cursor_variable%NOTFOUND
4.关闭游标变量
CLOSE cursor_variable;

使用游标变量处理查询多行结果集
下面的示例中,首先定义了一个游标类型,接下来定义游标变量,以及存放结果集的变量,动态查询语句将获得多个结果集。
OPEN cursorname FOR SELECT ... 时,其SELECT 语句使用了字符串变量(动态SQL),其后紧跟USING子句。
SQL>  declare
  2    type emp_cur_type is ref cursor;
  3    emp_cv  emp_cur_type;
  4    emp_record tb2%rowtype;
  5    sql_stat varchar2(100);
  6    v_empno tb2.empno%type := &inputno;
  7  begin
  8    sql_stat := 'select * from tb2 where empno = :no';   --动态多行结果集查询语句
  9    open emp_cv for sql_stat using v_empno;   --OPEN时使用动态查询语句以及USING子句来传递参数
 10    loop
 11      fetch emp_cv into emp_record;     --从结果集中提取记录
 12      exit when emp_cv%notfound;
 13      dbms_output.put_line('Employee Name: ' || emp_record.ename || ' ,Salary: ' || emp_record.sal);
 14    end loop;
 15* end;
Enter value for inputno: 7900
old   6:   v_empno tb2.empno%type := &inputno;
new   6:   v_empno tb2.empno%type := 7900;
Employee Name: JAMES ,Salary: 1045
PL/SQL procedure successfully completed.
BULK子句和动态SQL的使用
动态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中T的多行查询可以加快处理速度,从而提高应用程序的性能。当使用bulk子句时,集合类型可以是PL/SQL所支持的索引表、嵌套表和VARRY,但集合元 素必须使用SQL数据类型。常用的三种语句支持BULK子句,分别为EXECUTE IMMEDIATE,   FETCH 和FORALL。
下面的例子,首先定义了两个表类型以及其变量,接下来使用动态SQL语句来更新tb2的薪水,使用EXECUTE IMMEDIATE配合BULK COLLEC INTO 来处理结果集。
SQL>  declare
  2    type ename_table_type is table of tb2.ename%type index by binary_integer;
  3    type sal_table_type is table of tb2.sal%type index by binary_integer;
  4       ename_table ename_table_type;
  5       sal_table sal_table_type;
  6       sql_stat varchar2(200);
  7       v_percent number := &percent;
  8       v_dno     number := &dno;
  9  begin
 10    sql_stat := 'update tb2 set sal = sal * (1 + :percent/100)'
 11          || ' where deptno = :dno'
 12          || ' returning ename,sal into :name,:salary';
 13    execute immediate sql_stat using v_percent,v_dno
 14    returning bulk collect into ename_table,sal_table;
 15    for i in 1..ename_table.count loop
 16      dbms_output.put_line('Employee ' || ename_table(i) || ' Salary is: ' || sal_table(i));
 17    end loop;
 18* end;
Enter value for percent: 10
old   7:      v_percent number := &percent;
new   7:      v_percent number := 10;
Enter value for dno: 20
old   8:      v_dno     number := &dno;
new   8:      v_dno     number := 20;
Employee SMITH Salary is: 880
Employee JONES Salary is: 3272.5
Employee SCOTT Salary is: 3300
Employee ADAMS Salary is: 1210
Employee FORD Salary is: 3300
PL/SQL procedure successfully completed.

使用EXECUTE IMMEDIATE 结合BULK子句处理多行查询。下面示例中,与前一个示例相同,只不过其动态SQL有查询语句组成,且返回多个结果集,同样使用了BULK COLLECT INTO来传递结果。
SQL>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);
     v_dno     number := &dno;
begin
  sql_stat := 'select ename,sal from tb2 where deptno = :v_dno';     --动态DQL语句,未使用RETURNING子句
  execute immediate sql_stat bulk collect into ename_table,sal_table using v_dno;
  for i in 1..ename_table.count loop
    dbms_output.put_line('Employee ' || ename_table(i) || ' Salary is: ' || sal_table(i));
  end loop;
end;

Enter value for dno: 20
old   7:      v_dno     number := &dno;
new   7:      v_dno     number := 20;
Employee SMITH Salary is: 880
Employee JONES Salary is: 3272.5
Employee SCOTT Salary is: 3300
Employee ADAMS Salary is: 1210
Employee FORD Salary is: 3300
PL/SQL procedure successfully completed.

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

下面的示例中首先定义了游标类型,游标变量以及复合类型,复合变量,接下来从动态SQL中OPEN游标,然后使用FETCH将结果存放到复合变量中。即使用OPEN,FETCH代替了EXECUTE IMMEDIATE来完成动态SQL的执行。
SQL> 
declare
  2    type empcurtype is ref cursor;
  3    emp_cv empcurtype;
  4    type ename_table_type is table of tb2.ename%type index by binary_integer;
  5    ename_table ename_table_type;
  6    sql_stat varchar2(120);
  7  begin
  8    sql_stat := 'select ename from tb2 where deptno = :dno';
  9    open emp_cv for sql_stat using &dno;
 10    fetch emp_cv bulk collect into ename_table;
 11    for i in 1..ename_table.count loop
 12      dbms_output.put_line('Employee Name: ' || ename_table(i));
 13    end loop;
 14    close emp_cv;
 15*   end;Enter value for dno: 20
old   9:   open emp_cv for sql_stat using &dno;
new   9:   open emp_cv for sql_stat using 20;
Employee Name: SMITH
Employee Name: JONES
Employee Name: SCOTT
Employee Name: ADAMS
Employee Name: FORD
PL/SQL procedure successfully completed.

在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 来提取结果集。
SQL> declare
  2    type ename_table_type is table of tb2.ename%type;
  3    type sal_table_type is table of tb2.sal%type;
  4         ename_table ename_table_type;
  5         sal_table sal_table_type;
  6         sql_stat varchar2(100);
  7  begin
  8    ename_table := ename_table_type('BLAKE','FORD','MILLER');
  9    sql_stat := 'update tb2 set sal = sal * 1.1 where ename = :1'
 10            || ' returning sal into :2';
 11    forall i in 1..ename_table.count
 12      execute immediate sql_stat using ename_table(i) returning bulk collect into sal_table;
 13    for j in 1..sal_table.count loop
 14      dbms_output.put_line('The ' || ename_table(j) || '''' || 's new salalry is ' || sal_table(j));
 15    end loop;
 16*   end;

The BLAKE's new salalry is 3448.5
The FORD's new salalry is 3993
The MILLER's new salalry is 1573
PL/SQL procedure successfully completed.
常见错误

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

SQL> declare
  2    sql_stat varchar2(100);
  3    v_deptno varchar2(5) := '30';
  4  begin
  5    sql_stat := 'create table tb_emp ' || ' as select * from emp '
  6            || ' where deptno = :dno';
  7    execute immediate sql_stat using v_deptno;
  8  end;
  9  /
declare
*
ERROR at line 1:
ORA-01027: bind variables not allowed for data definition operations
ORA-06512: at line 7
解决办法,将绑定变量直接拼接,如下:
sql_stat := 'CREATE TABLE tb_tmp ' || 'AS SELECT * FROM scott.emp ' || 'WHERE deptno = ' || v_deptno;
2、不能使用schema对象作为绑定参数,下面的示例中,动态SQL语句查询需要传递表名,因此收到了错误提示。

SQL> declare
  sql_stat varchar2(100);

  v_tbname varchar2(20);
  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;

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

EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM ' || v_tbname into v_count;

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

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

SQL> declare
  sql_stat varchar2(100);
  v_count number;
begin
  execute immediate 'select count(*) from emp;'    --此处多出了分号,应该去掉
  into v_count;
  dbms_output.put_line('The table record is ' || v_count);
end;
4、动态PL/SQL块不能使用正斜杠来结束块,但是块结尾处必须要使用分号(;)
SQL> 
declare
  2    plsql_block varchar2(300);
  3  begin
  4    plsql_block := 'Declare ' ||
  5                 ' v_date date; ' ||
  6                 ' begin ' ||
  7                     ' select sysdate into v_date from dual; ' ||
  8                     ' dbms_output.put_line(to_char(v_date,''yyyy-mm-dd'')); ' ||
  9                 ' end;
 10                   /';      --此处多出了/,应该将其去掉
 11    execute immediate plsql_block;
 12* end;declare
*
ERROR at line 1:
ORA-06550: line 2, column 18:
PLS-00103: Encountered the symbol "/" The symbol "/" was ignored.
ORA-06512: at line 11

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

SQL> declare
  2    sql_stat varchar2(200);
  3    v_empno number := 7900;
  4  begin
  5    sql_stat := 'update tb2 set sal = 1.1 * :new_sal where empno = :eno';
  6    execute immediate sql_stat using null,v_empno;   
--此处不能直接使用NULL

  7  end;
  8  /
  execute immediate sql_stat using null,v_empno;
                                   *
ERROR at line 6:
ORA-06550: line 6, column 36:
PLS-00457: expressions have to be of SQL types
ORA-06550: line 6, column 3:
PL/SQL: Statement ignored

正确的处理方法

SQL> declare
  2    sql_stat varchar2(200);
  3    v_empno number := 7900;
  4    v_sal tb2.sal%type;   --声明一个新变量,但不赋值
  5  begin
  6    sql_stat := 'update tb2 set sal = 1.1 * :new_sal where empno = :eno';
  7    execute immediate sql_stat using v_sal,v_empno;
  8    commit;
  9  end;
 10  /
PL/SQL procedure successfully completed.

5、日期和字符型必须要使用引号来处理
下面的示例中,使用了日期型变量,未使用引号标注,且使用了变量绑定,但直接输入日期型数据,而不加引号,则收到错误提示。

SQL>  DECLARE
  2           sql_stat  VARCHAR2(100);
  3           v_date    DATE :=&dt;
  4           v_empno   NUMBER :=7900;
  5           v_ename   tb2.ename%TYPE;
  6           v_sal     tb2.sal%TYPE;
  7         BEGIN
  8           sql_stat := 'SELECT ename,sal FROM tb2 WHERE hiredate=:v_date';
  9           EXECUTE IMMEDIATE sql_stat
 10           INTO v_ename,v_sal
 11           USING v_date;
 12           DBMS_OUTPUT.PUT_LINE('Employee Name '||v_ename||', sal is '||v_sal);
 13*        END;
 14  /
Enter value for dt: 1987-05-23
old   3:          v_date    DATE :=&dt;
new   3:          v_date    DATE :=1987-05-23;
         v_date    DATE :=1987-05-23;
                          *
ERROR at line 3:
ORA-06550: line 3, column 27:
PLS-00382: expression is of wrong type
ORA-06550: line 3, column 20:
PL/SQL: Item ignored
ORA-06550: line 11, column 16:
PLS-00320: the declaration of the type of this expression is incomplete or malformed
ORA-06550: line 9, column 10:
PL/SQL: Statement ignored

处理办法一
执行时输入带引号的字串
SQL> /
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语句不使用绑定日期变量,而是将其连接成字符串,则可以使用下面的方式来实现

如果上面的例子中,动态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;

6、单行SELECT 查询不能使用RETURNING INTO返回
下面的示例中,使用了动态的单行SELECT查询,并且使用了RETURNING子句来返回值。事实上,RETURNING coloumn_name INTO 子句仅仅支持对DML结果集的返回,因此,收到了错误提示。SQL>declare
  2    sql_stat varchar2(100);
  3    v_empno  tb2.empno%type := &empno;
  4    v_ename  tb2.ename%type;
  5  begin
  6    sql_stat := ' select ename from tb2 where empno = :eno';
  7    execute immediate sql_stat using v_empno returning ename into v_ename;
  8    dbms_output.put_line('Employee name: ' || v_ename);
  9  end;
 10  /
Enter value for empno: 7900
old   3:   v_empno  tb2.empno%type := &empno;
new   3:   v_empno  tb2.empno%type := 7900;
  execute immediate sql_stat using v_empno returning ename into v_ename;
                                                     *
ERROR at line 7:
ORA-06550: line 7, column 54:
PLS-00103: Encountered the symbol "ENAME" when expecting one of the following:
into bulk
The symbol "ENAME" was ignored.
上,RETURNING coloumn_name INTO

解决方式

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

execute immediate sql_stat into v_ename using v_empno;


参考至:http://blog.csdn.net/robinson_0612/article/details/6118010
              http://blog.csdn.net/robinson_0612/article/details/6118049

转载于:http://czmmiao.iteye.com/blog/1822979

posted on 2013-03-06 09:51  restService  阅读(375)  评论(0编辑  收藏  举报

导航


我是有底线的赠送场