【转】Oracle执⾏动态语句
1.静态SQLSQL与动态SQL
oracle游标的使用Oracle编译PL/SQL程序块分为两个种:其⼀为前期联编(early binding),即SQL语句在程序编译期间就已经确定,⼤多数的编译情况属于这种类型;另外⼀种是后期联编(late binding),即SQL语句只有在运⾏阶段才能建⽴,例如当查询条件为⽤户输⼊时,那么Oracle的SQL 引擎就⽆法在编译期对该程序语句进⾏确定,只能在⽤户输⼊⼀定的查询条件后才能提交给SQL引擎进⾏处理。通常,静态SQL采⽤前⼀种编译⽅式,⽽动态SQL采⽤后⼀种编译⽅式。
2.动态SQL程序开发
理解了动态SQL编译的原理,也就掌握了其基本的开发思想。动态SQL既然是⼀种”不确定”的SQL,那其执⾏就有其相应的特点。Oracle中提供了Execute immediate语句来执⾏动态SQL,语法如下:
Excute immediate 动态SQL语句 using 绑定参数列表 returning into输出参数列表;
1)动态SQL是指DDL和不确定的DML(即带参数的DML)
2)绑定参数列表为输⼊参数列表,即其类型为in类型,在运⾏时刻与动态SQL语句中的参数(实际上占位符,可以理解为函数⾥⾯的形式参数)进⾏绑定。
3)输出参数列表为动态SQL语句执⾏后返回的参数列表。
4)由于动态SQL是在运⾏时刻进⾏确定的,所以相对于静态⽽⾔,其更多的会损失⼀些系统性能来换取其灵活性。
设数据库的emp表,其数据为如下:
ID NAME SALARY
100Jacky5600
101Rose3000
102John4500
要求:
1.创建该表并输⼊相应的数据。
2.根据特定ID可以查询到其姓名和薪⽔的信息。
3.根据⼤于特定的薪⽔的查询相应的员⼯信息。
根据前⾯的要求,可以分别创建三个过程(均使⽤动态SQL)来实现:
过程⼀:
1create or replace procedure create_table is
2begin
3execute immediate ' create table emp(
4id number,
5name varchar2(10),
6salary number )'; --动态SQL为DDL语句
7end create_table;
过程⼆:
1create or replace procedure find_info(p_id number) i s
2 v_name varchar2(10);
3 v_salary number;
4begin
5execute immediate '
6select name,salary from emp
7where id=:1'
8 using p_id
9 returning into v_name,v_salary; --动态SQL为查询语句
10 dbms_output.put_line(v_name ||'的收⼊为:'||to_char(v_salary));
11 exception
12when others then
13 dbms_output.put_line('不到相应数据');
14end find_info;
过程三:
1create or replace procedure find_emp(p_salary number) i s
2 r_emp emp%rowtype;
3 type c_type is ref cursor;
4 c1 c_type;
5begin
6open c1 for'
7select * from emp
8where salary >:1'
9 using p_salary;
10 loop
11fetch c1 into r_emp;
12exit when c1%notfound;
13 dbms_output.put_line('薪⽔⼤于‘||to_char(p_salary)||'的员⼯为:‘);
14 dbms_output.put_line('ID为'to_char(r_emp)||' 其姓名为:'||r_emp.name);
15end loop;
16close c1;
17end create_table;
注意:在过程⼆中的动态SQL语句使⽤了占位符“:1“,其实它相当于函数的形式参数,使⽤”:“作为前缀,然后使⽤using语句将p_id在运⾏时刻将:1给替换掉,这⾥p_id相当于函数⾥的实参。另外过程三中打开的游标为动态游标,它也属于动态SQL的范畴,其整个编译和开发的过程与execute immediate
执⾏的过程很类似,这⾥就不在赘述了。
3.动态SQL语句开发技巧
前⾯分析到了,动态SQL的执⾏是以损失系统性能来换取其灵活性的,所以对它进⾏⼀定程度的优化也是必要的,笔者根据实际开发经验给出⼀些开发的技巧,需要指出的是,这⾥很多经验不仅局限于动态SQL,有些也适⽤于静态SQL,在描述中会给予标注。
技巧⼀:尽量使⽤类似的SQL语句,这样Oracle本⾝通过SGA中的共享池来直接对该SQL语句进⾏缓存,那么在下⼀次执⾏类似语句时就直接调⽤缓存中已解析过的语句,以此来提⾼执⾏效率。
技巧⼆:当涉及到集合单元的时候,尽量使⽤批联编。⽐如需要对id为100和101的员⼯的薪⽔加薪10%,⼀般情况下应该为如下形式:
1declare
2 type num_list is varray(20) of number;
3 v_id num_list :=num_list(100,101);
4begin
5 ...
6for i in v_id.first .. v_id.last loop
7 ...
8execute immediate 'update emp
9set =salary*1.2
10where id=:1 '
11 using v_id(i);
12end loop;
13end;
对于上⾯的处理,当数据量⼤的时候就会显得⽐较慢,那么如果采⽤批联编的话,则整个集合⾸先⼀次性的传⼊到SQL引擎中进⾏处理,这样⽐单独处理效率要⾼的多,进⾏批联编处理的代码如下:
1declare
2 type num_list is varray(20) of number;
3 v_id num_list :=num_list(100,101);
4begin
5 ...
6 forall i in v_id.first .. v_id.last loop
7 ...
8execute immediate 'update emp
9set =salary*1.2
10where id=:1 '
11 using v_id(i);
12end loop;
13end;
这⾥是使⽤forall来进⾏批联编,这⾥将批联编处理的情形作⼀个⼩结:
1) 如果⼀个循环内执⾏了insert,delete,update等语句引⽤了集合元素,那么可以将其移动到⼀个forall语句中。
2) 如果select into,fetch into 或returning into ⼦句引⽤了⼀个集合,应该使⽤bulk collect ⼦句进⾏合并。
3) 如有可能,应该使⽤主机数组来实现在程序和数据库服务器之间传递参数。
技巧三:使⽤NOCOPY编译器来提⾼PL/SQL性能。缺省情况下,out类型和in out类型的参数是由值传递的⽅式进⾏的。但是对于⼤的对象类型或者集合类型的参数传递⽽⾔,其希望损耗将是很⼤的,为了减少损耗,可以采⽤引⽤传递的⽅式,即在进⾏参数声明的时候引⽤NOCOPY关键字来说明即可到达这样的效果。⽐如创建⼀个过程:
create or replace procedure test(p_object in nocopy square)...end;
其中square为⼀个⼤的对象类型。这样只是传递⼀个地址,⽽不是传递整个对象了。显然这样的处理也是提⾼了效率。
4.⼩结
本⽂对动态SQL的编译原理、开发过程以及开发技巧的讨论,通过本⽂的介绍后,相信读者对动态SQL程序开发有了⼀个总体的认识,为今后深⼊的⼯作打下⼀个良好的基础。
⼀、为什么要使⽤动态执⾏语句?
由于在PL/SQL 块或者存储过程中只⽀持DML语句及控制流语句,并不⽀持DDL语句,所以Oracle动态执⾏语句便应允⽽⽣了。关于DDL与DML的区别,请参见:。
⼆、动态执⾏语句怎么⽤?
动态执⾏语句代替了Oracle 8i中的DBMS_SQL Package包。
1)在PL/SQL中运⾏SQL语句,例如:
⽰例⼀:
BEGIN
EXECUTE IMMEDIATE 'select count(username) from user_users'; --DML(每条语句必须以分号结
尾)
END;
⽰例⼆:
BEGIN
EXECUTE IMMEDIATE 'ALTER TABLE a RENAME TO EXAMPLE'; --DDL
END;
你可能会问不是只DDL语句需要⽤动态语句执⾏吗?是的,你说的完全正确。但是DML语句⽤动态语句执⾏也可以。
即:DDL语句只能⽤动态执⾏语句来执⾏,DML语句亦可⽤动态语句来执⾏。
2)使⽤using给动态语句传值,例如:
DECLARE
e_name VARCHAR2(10); --声明变量e_name
e_age INT; --声明变量e_age
BEGIN
e_name := 'sillylaura'; --给变量e_name赋值
e_age := 21; --给变量e_age 赋值
EXECUTE IMMEDIATE 'insert into Example values(seq_add_val,:2,:3)' using e_name,e_age; --DML END; 3)使⽤动态语句赋值(select 列名 into 变量 from ……)
DECLARE
temp INT;
BEGIN
EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM dual' INTO temp; --DML
dbms_output.put_line(temp);
END;
4)传递并检索值:into⽤在using之前。
DECLARE
temp INT;
test VARCHAR2(10);
BEGIN
test := 'ok';
EXECUTE immediate 'SELECT COUNT(*) FROM dual where dummy = :1 GROUP BY dummy' INTO temp USING test;
dbms_output.put_line(temp ||' '|| test);
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line('It has no data!');
END;
三、动态语句⼩结
1. DDL语句只能⽤动态执⾏语句来执⾏,DML语句亦可⽤动态语句来执⾏。
2. 在使⽤select……into⼦句为变量赋值时,into字句必须写在单引号外⾯。
3. 在同时使⽤select……into⼦句和using时,注意⼆者的顺序:into⽤在using之前,且都在单引号外⾯。
4. 注意写上必要的异常错误处理。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论