实际中,使用动态SQL创建中间表,将数据汇总到·中间表后执行逻辑运算,再删除这张表。
这里的实例是创建表后读取数据并打印出来,最后删除数据。
SQL> l
1 declare
2 sql_states varchar2(5000);
3 counter number;
4 begin
5 sql_states :='create table pid '||
6 'as select object_id,object_name from dba_objects where rownum<11';
7 execute immediate sql_states;
8 execute immediate 'select count(distinct(object_id)) from pid '
9 into counter;
10 dbms_output.put_line('counter is '||counter);
11* end;
SQL> /
counter is 10
||
符号; -注意换行时是否需要 空格来分隔字符,不然SQL执行语句将报错ORA-00933:SQL命令未正确结束
示例:
SQL> l
1 declare
2 obj_id number :=28;
3 obj_name varchar2(20);
4 plsql_blk varchar2(200);
5 begin
6 plsql_blk := ' declare var_date date;' ||
7 'begin ' || #注意begin后没有;结尾,注意加上空格分隔语句
8 'select sysdate into var_date from dual;' ||
9 'dbms_output.put_line(var_date); '||
10 'end;';
11 execute immediate plsql_blk;
12* end;
SQL> /
24-3月 -20
动态的输入输出SQL语句操作的数据时
SQL> l
1 declare obj_id number := 28;
2
3 obj_name varchar2(20);
4
5 name varchar2(20) := 'mark';
6
7 sql_states varchar2(200);
8 #动态SQL语句包含三个绑定变量
# “:x” 对应设置的新的object_name的值
# “:y” 绑定要修改的object_id
# “:z”绑定更新后的值到某个具体变量
9 begin sql_states := 'update pid set object_name = :x where object_id = :y ' ||
10 'returning object_name into :z';
11 # 使用using子句按照顺序向SQL语句传递给绑定变量“:x”和 “:y”
# 再到具体变量object_name、object_name
# 使用returning into 子句将更新后的object_name值传递给obj_name变量
12 execute immediate sql_states using name,
13 obj_id returning into obj_name;
14
15 dbms_output.put_line('obj_name is ' || obj_name);
16
17 end;
18*
SQL> /
obj_name is mark
PL/SQL 过程已成功完成。
EXECUTE IMMEDIATE 动态SQL语句
[ INTO variable1,variable2...]
[USING [ IN | OUT | IN OUT]绑定参数1,绑定参数2...]
[{RETURNING | RETURN } 属性值1,属性值2, ... into 绑定参数1,绑定参数2...]
DDL(Data Definition Language)
CREATE
、ALTER
、DROP
等,DDL主要是用在定义或改变表(TABLE)的结构,数据类型,表之间的链接和约束等初始化工作;例如:
sql_states := 'create table pid '||
'as select object_id,object_name from dba_objects '||
where object_id = :id';
使用了绑定变量:id
,其对应了object_id,表示在执行时在绑定这个值,并依据这个值创建表PID。
执行将报错:
ORA-01027: 在数据定义操作中不允许有绑定变量
静态SQL使用分号(;
)作为结束标志,动态不可以
例如:
sql_states := 'create table pid '||
'as select object_id,object_name from dba_objects ;' ;
执行结果将会报错ORA-00911: 无效字符
因为EXECUTE IMMEDIATE
已经是执行命令,不需加右斜线执行该语句。
例如:
plsql_blk := ' declare var_date date;' ||
'begin ' ||
'select sysdate into var_date from dual;' ||
'dbms_output.put_line(var_date); '||
'end; /';
执行将报错:
PLS-00103: 出现符号 "/"符号 "/" 被忽略。
using
子句中不能使用NULL作为动态语句的输入参数
例如:
SQL> declare
2 sql_states varchar2(200);
3 begin
4 sql_states := 'update pid set object_name = :name_null';
5 execute immediate sql_states using null;
6 end;
7 /
execute immediate sql_states using null;
*
第 5 行出现错误:
ORA-06550: 第 5 行, 第 37 列:
PLS-00457: 表达式必须是 SQL 类型
ORA-06550: 第 5 行, 第 2 列:
PL/SQL: Statement ignored
将using null部分修改为:先声明一个varchar2类型的变量name,不赋值,再将此变量 传递给USING子句,则达到了传入空值的目的。
SQL> l
1 declare
2 sql_states varchar2(200);
3 name varchar2(20);
4 begin
5 sql_states := 'update pid set object_name = :name_null';
6 execute immediate sql_states
7 using name;
8* end;
SQL> /
PL/SQL 过程已成功完成。
例如:
execute immediate 'select count(*) from :table_name '
语句中绑定了变量:table_name
,运行时将报错:ORA-00903: 表名无效
declare
type type1 is table of number index by pls_integer;
type type2 is table of varchar2(20) index by pls_integer;
num_type1 type1;
text_type2 type2;
var_start integer;
var_end integer;
var_dura number(2,5);
begin
# FOR循环生成数据
for i in 1..50000 loop
num_type1(i) :=i;
text_type2(i) :='type'||i;
end loop;
# dbms_utility.get_time可返回精度为0.01秒的当前时间
var_start := dbms_utility.get_time;
# FOR循环插入数据
for i in 1..50000 loop
insert into test
values (num_type1(i),text_type2(i));
commit;
end loop;
# truncate table test 清空表test数据,保留表结构
execute immediate 'truncate table test';
var_end := dbms_utility.get_time;
dbms_output.put_line('FOR :'||(var_end - var_start));
var_start := dbms_utility.get_time;
#FORALL循环插入数据
forall i in 1..50000
insert into test
values (num_type1(i),text_type2(i));
commit;
var_end := dbms_utility.get_time;
dbms_output.put_line('FORALL :'||(var_end - var_start));
end;
执行结果:
FOR :674
FORALL :8
实例:
通过loop循环给集合赋值,再删除集合中的部分数据生成稀疏集合,使用indices of 向表中插入数据。
SQL> l
1 declare
2 type type1 is table of number index by pls_integer;
3 type type2 is table of varchar2(20) index by pls_integer;
4 num_type1 type1;
5 text_type2 type2;
6 var_count number;
7 begin
8 execute immediate 'truncate table test';
9 for i in 1..6 loop
10 num_type1(i) :=i;
11 text_type2(i) :='type'||i;
12 end loop;
13 #删掉部分数据,必须将集合对应索引位置一起删除,否则报错
#删除后形成了稀疏集合
14 text_type2.delete(2);
15 num_type1.delete(2);
16 text_type2.delete(4);
17 num_type1.delete(4);
18
19
20 forall i in indices of num_type1
21 insert into test
22 values (num_type1(i),text_type2(i));
23 commit;
24 select count(*)
25 into var_count
26 from test;
27 dbms_output.put_line('var_count :'||var_count);
28 end;
29*
SQL> /
var_count :4 #6-2=4插入了稀疏集合中的剩下数据
直接看实例:
SQL> l
1 declare
2 type type1 is table of number index by pls_integer;
3 type type2 is table of varchar2(20) index by pls_integer;
4 type type3 is table of pls_integer index by pls_integer;
5
6 num_type1 type1;
7 text_type2 type2;
8 count_type3 type3;
9 var_count number;
10 begin
11 for i in 1..6 loop
12 num_type1(i) :=i;
13 text_type2(i) :='type'||i;
14 count_type3(i) :=i;
15 dbms_output.put_line('count_type3('||i||') : '||count_type3(i));
16 end loop;
17 count_type3.delete(3);#删除第三个数
18 execute immediate 'truncate table test';
19 forall i in values of count_type3
20 insert into test
21 values (num_type1(i),text_type2(i));
22 commit;
23 select count(*)
24 into var_count
25 from test;
26 dbms_output.put_line('var_count :'||var_count);
27 end;
SQL> /
count_type3(1) : 1
count_type3(2) : 2
count_type3(3) : 3
count_type3(4) : 4
count_type3(5) : 5
count_type3(6) : 6
var_count :5
这样看还不够直观,直接看插入的test表中的数据就可得知:
SQL> l
1* select * from test
SQL> /
NUM_TYPE1 TEXT_TYPE2
---------- ------------------
1 type1
2 type2
4 type4
5 type5
6 type6
由于索引集合count_type3
中没有3,所以根据此索引集合,数据不会插入索引为3的数据。
例如查询工资大于1000的员工信息,使用游标是:
先获取记录的集合,再用for循环依次获得数据记录
SQL> l
1 declare
2 cursor emp_cur is
3 select empno,ename,sal
4 from emp
5 where sal > 1000;
6 begin
7 for e_cur in emp_cur loop
8 dbms_output.put_line(e_cur.empno||' '||e_cur.ename||' '||e_cur.sal);
9 end loop;
10* end;
使用BULK COLLECT
批量处理检索的数据时,需先定义集合类型
SQL> l
1 declare
#集合定义的数据类型需与select选的列数据类型一致
2 type type1 is table of emp.empno%type;
3 type type2 is table of emp.ename%type;
4 type type3 is table of emp.sal%type;
5
6 type1_tab type1;
7 type2_tab type2;
8 type3_tab type3;
9
10 begin
# 使用 bulk collect into批量填充数据
# 直接into会报错 PLS-00642: 在 SQL 语句中不允许使用本地收集类型
11 select empno,ename,sal
12 bulk collect into type1_tab,type2_tab,type3_tab
13 from emp
14 where sal>2000;
#调用集合type1_tab的属性first、last作为索引值
15 for i in type1_tab.first .. type1_tab.last
16 loop
17 dbms_output.put_line('type1_tab :'||type1_tab(i)||' type2_tab :'||type2_tab(i)||' type3_tab :'||type3_tab(i));
18
19 end loop;
20 end;
21*
SQL> /
type1_tab :7566 type2_tab :JONES type3_tab :2975
·······省略·······
type1_tab :7839 type2_tab :KING type3_tab :5000
type1_tab :7902 type2_tab :FORD type3_tab :3000
PL/SQL 过程已成功完成。
但是如果查询的数据集为空时,会报错
SQL> 14 where sal>20000;;
SQL> /
declare
*
第 1 行出现错误:
ORA-06502: PL/SQL: 数字或值错误
ORA-06512: 在 line 15
可通过集合的属性来判断几何中是否有返回数据;
另外,返回数据过多时,可在loop循环中加入LIMIT
关键字限制每次读取的行数。
SQL> l
1 declare
2 cursor emp_cur is
3 select empno,ename,sal from emp
4 where sal>200000;#定义一个游标
5 type type1 is table of emp.empno%type;
6 type type2 is table of emp.ename%type;
7 type type3 is table of emp.sal%type;
8
9 type1_tab type1;
10 type2_tab type2;
11 type3_tab type3;
12 var_limit pls_integer :=20;
13
14 begin
15 open emp_cur;
16 loop
17 fetch emp_cur
18 bulk collect into type1_tab,type2_tab,type3_tab
19 limit var_limit;#limit 20 限制返回数量一次为20
20 exit when type1_tab.count = 0;#没有返回值时退出当前执行语句块
21 for i in type1_tab.first .. type1_tab.last
22 loop
23 dbms_output.put_line('type1_tab :'||type1_tab(i)||' type2_tab :'||type2_tab(i)||' type3_tab :'||type3_tab(i));
24
25 end loop;
26 end loop;
27 close emp_cur;
28* end;
SQL> /
PL/SQL 过程已成功完成。
重点介绍了动态SQL语句,动态SQL语句更加灵活,适应实际业务需求。
包括: