PL/SQL模块学习之十八:PL/SQL中的SQL

文章目录

    • 1.动态SQL
      • 1.1有效的SQL语句
      • 1.2 SQL语句块
      • 1.3 USING和RETURNING INTO子句
      • 1.4 EXECUTE IMMEDIATE
        • 1.4.1 动态SQL语句结构
        • 1.4.2 DDL语句不能使用绑定变量
        • 1.4.3 动态SQL语句结尾不使用分号
        • 1.4.4 动态SQL语句块结尾不使用右斜线
        • 1.4.5 动态SQL语句USING子句空值处理
        • 1.4.6 select语句中那表名不能使用绑定变量
      • 1.5 FORALL实现语句批处理
        • 1.5.1 INDICES OF处理稀疏集合
        • 1.5.2 VALUES OF 提供索引值
        • 1.5.3 BULK COLLECT 实现批量检索结果
    • 小结:

1.动态SQL

1.1有效的SQL语句

实际中,使用动态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

1.2 SQL语句块

  • 语句块直接的连接用||符号; -注意换行时是否需要 空格来分隔字符,不然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

1.3 USING和RETURNING INTO子句

动态的输入输出SQL语句操作的数据时

  • 使用using子句向动态SQL语句传递数据
  • 使用returning子句将值传递到into的变量中
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 过程已成功完成。

1.4 EXECUTE IMMEDIATE

1.4.1 动态SQL语句结构

EXECUTE IMMEDIATE 动态SQL语句
[ INTO variable1,variable2...]
[USING [ IN | OUT | IN OUT]绑定参数1,绑定参数2...]
[{RETURNING | RETURN } 属性值1,属性值2... into 绑定参数1,绑定参数2...]

1.4.2 DDL语句不能使用绑定变量

  • 数据库模式定义语言DDL(Data Definition Language)
    CREATEALTERDROP等,DDL主要是用在定义或改变表(TABLE)的结构,数据类型,表之间的链接和约束等初始化工作;
    DDL操作是隐性提交的,不能rollback;

例如:

sql_states := 'create table pid '||
  'as select object_id,object_name from dba_objects '||
  where object_id = :id';

使用了绑定变量:id,其对应了object_id,表示在执行时在绑定这个值,并依据这个值创建表PID。

执行将报错:

ORA-01027: 在数据定义操作中不允许有绑定变量

1.4.3 动态SQL语句结尾不使用分号

静态SQL使用分号()作为结束标志,动态不可以
例如:

sql_states := 'create table pid '||
  'as select object_id,object_name from dba_objects ;' ;

执行结果将会报错ORA-00911: 无效字符

1.4.4 动态SQL语句块结尾不使用右斜线

因为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: 出现符号 "/"符号 "/" 被忽略。

1.4.5 动态SQL语句USING子句空值处理

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 过程已成功完成。

1.4.6 select语句中那表名不能使用绑定变量

例如:

execute immediate 'select count(*) from :table_name '

语句中绑定了变量:table_name,运行时将报错:ORA-00903: 表名无效

1.5 FORALL实现语句批处理

  • forall向SQL引擎发送一个SQL语句即可执行多次
  • for循环需要多次发送SQL语句
    实例:
    测试for循环与forall批处理的效率
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

1.5.1 INDICES OF处理稀疏集合

实例:
通过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插入了稀疏集合中的剩下数据

1.5.2 VALUES 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      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的数据。

1.5.3 BULK COLLECT 实现批量检索结果

  • 可实现批量检索结果,从而将结果一并发送到 PL/SQL引擎
  • 游标可对查询结果的多条记录处理,一次处理一行记录

例如查询工资大于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语句更加灵活,适应实际业务需求。
包括:

  1. execute immediate 执行动态语句(语法和注意事项)
  2. using 和returning into 的使用。(类似Java的输入输出值)
  3. forall 实现批量处理(提升效率)

你可能感兴趣的:(oracle,PL/SQL)