PL/SQL模塊學習之十八:PL/SQL中的SQL

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 實現批量處理(提升效率)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章