文章目錄
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)
即CREATE
、ALTER
、DROP
等,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語句更加靈活,適應實際業務需求。
包括:
- execute immediate 執行動態語句(語法和注意事項)
- using 和returning into 的使用。(類似Java的輸入輸出值)
- forall 實現批量處理(提升效率)