在Oracle中執行動態SQL的幾種方法
在一般的sql操作中,sql語句基本上都是固定的,如:
SELECT t.empno,t.ename FROM scott.emp t WHERE t.deptno = 20;
但有的時候,從應用的需要或程序的編寫出發,都可能需要用到動態SQl,如:
當 from 后的表 不確定時,或者where 后的條件不確定時,都需要用到動態SQL。
一、使用動態游標實現
1、聲明動態游標
TYPE i_cursor_type IS REF CURSOR;
2、聲明游標變量
my_cursor i_cursor_type;
3、使用游標
n_deptno:=20;
dyn_select := 'select empno,ename from emp where deptno='||n_deptno;
OPEN my_cursor FOR dyn_select;
LOOP
FETCH my_cursor INTO n_empno,v_ename;
EXIT WHEN my_cursor%NOTFOUND;
--用n_empno,v_ename做其它處理
--....
END LOOP;
CLOSE dl_cursor;
4、小結:動態游標可以勝任大多數動態SQL的需求了,使用簡潔方便居家旅行之必備殺人放火之法寶。
二、使用 EXECUTE IMMEDIATE
最早大家都使用DBMS_SQL包,但是太太麻煩了,最終都放棄了。但是自從有了EXECUTE IMMEDIATE之后,但要注意以下幾點:
EXECUTE IMMEDIATE代替了以前Oracle8i中DBMS_SQL package包.它解析並馬上執行動態的SQL語句或非運行時創建的PL/SQL塊.動態創建和執行SQL語句性能超前,EXECUTE IMMEDIATE的目標在於減小企業費用並獲得較高的性能,較之以前它相當容易編碼.盡管DBMS_SQL仍然可用,但是推薦使用EXECUTE IMMEDIATE,因為它獲的收益在包之上。
使用技巧
1. EXECUTE IMMEDIATE將不會提交一個DML事務執行,應該顯式提交
如果通過EXECUTE IMMEDIATE處理DML命令,那么在完成以前需要顯式提交或者作為EXECUTE IMMEDIATE自己的一部分. 如果通過EXECUTE IMMEDIATE處理DDL命令,它提交所有以前改變的數據
2. 不支持返回多行的查詢,這種交互將用臨時表來存儲記錄(參照例子如下)或者用REF cursors.
3. 當執行SQL語句時,不要用分號,當執行PL/SQL塊時,在其尾部用分號.
4. 在Oracle手冊中,未詳細覆蓋這些功能。下面的例子展示了所有用到Execute immediate的可能方面.希望能給你帶來方便.
5. 對於Forms開發者,當在PL/SQL 8.0.6.3.版本中,Forms 6i不能使用此功能.
EXECUTE IMMEDIATE用法例子
1. 在PL/SQL運行DDL語句
begin
execute immediate 'set role all';
end;
2. 給動態語句傳值(USING 子句)
declare
l_depnam varchar2(20) := 'testing';
l_loc varchar2(10) := 'Dubai';
begin
execute immediate 'insert into dept values (:1, :2, :3)'
using 50, l_depnam, l_loc;
commit;
end;
3. 從動態語句檢索值(INTO子句)
declare
l_cnt varchar2(20);
begin
execute immediate 'select count(1) from emp'
into l_cnt;
dbms_output.put_line(l_cnt);
end;
4. 動態調用例程.例程中用到的綁定變量參數必須指定參數類型.黓認為IN類型,其它類型必須顯式指定
declare
l_routin varchar2(100) := 'gen2161.get_rowcnt';
l_tblnam varchar2(20) := 'emp';
l_cnt number;
l_status varchar2(200);
begin
execute immediate 'begin ' || l_routin || '(:2, :3, :4); end;'
using in l_tblnam, out l_cnt, in out l_status;
if l_status != 'OK' then
dbms_output.put_line('error');
end if;
end;
5. 將返回值傳遞到PL/SQL記錄類型;同樣也可用%rowtype變量
declare
type empdtlrec is record (empno number(4),ename varchar2(20),deptno number(2));
empdtl empdtlrec;
begin
execute immediate 'select empno, ename, deptno '||'from emp where empno = 7934'
into empdtl;
end;
6. 傳遞並檢索值.INTO子句用在USING子句前
declare
l_dept pls_integer := 20;
l_nam varchar2(20);
l_loc varchar2(20);
begin
execute immediate 'select dname, loc from dept where deptno = :1'
into l_nam, l_loc
using l_dept ;
end;
7. 多行查詢選項.對此選項用insert語句填充臨時表,用臨時表進行進一步的處理,也可以用REF cursors糾正此缺憾.
declare
l_sal pls_integer := 2000;
begin
execute immediate 'insert into temp(empno, ename) ' ||
' select empno, ename from emp ' ||
' where sal > :1'
using l_sal;
commit;
end;
對於處理動態語句,EXECUTE IMMEDIATE比以前可能用到的更容易並且更高效.當意圖執行動態語句時,適當地處理異常更加重要.應該關注於捕獲所有可能的異常.
Oracle 動態SQL
Oracle 動態SQL有兩種寫法:用 DBMS_SQL 或 execute immediate,建議使用后者。試驗步驟如下:
1. DDL 和 DML
- /*** DDL ***/
- begin
- EXECUTE IMMEDIATE 'drop table temp_1';
- EXECUTE IMMEDIATE 'create table temp_1(name varchar2(8))';
- end;
- /*** DML ***/
- declare
- v_1 varchar2(8);
- v_2 varchar2(10);
- str varchar2(50);
- begin
- v_1:='測試人員';
- v_2:='北京';
- str := 'INSERT INTO test (name ,address) VALUES (:1, :2)';
- EXECUTE IMMEDIATE str USING v_1, v_2;
- commit;
- end;
2. 返回單條結果
- declare
- str varchar2(500);
- c_1 varchar2(10);
- r_1 test%rowtype;
- begin
- c_1:='測試人員';
- str:='select * from test where name=:c WHERE ROWNUM=1';
- execute immediate str into r_1 using c_1;
- DBMS_OUTPUT.PUT_LINE(R_1.NAME||R_1.ADDRESS);
- end ;
3. 返回結果集
- CREATE OR REPLACE package pkg_test as
- /* 定義ref cursor類型
- 不加return類型,為弱類型,允許動態sql查詢,
- 否則為強類型,無法使用動態sql查詢;
- */
- type myrctype is ref cursor;
- --函數申明
- function get(intID number) return myrctype;
- end pkg_test;
- /
- CREATE OR REPLACE package body pkg_test as
- --函數體
- function get(intID number) return myrctype is
- rc myrctype; --定義ref cursor變量
- sqlstr varchar2(500);
- begin
- if intID=0 then
- --靜態測試,直接用select語句直接返回結果
- open rc for select id,name,sex,address,postcode,birthday from
- student;
- else
- --動態sql賦值,用:w_id來申明該變量從外部獲得
- sqlstr := 'select id,name,sex,address,postcode,birthday from student
- where id=:w_id';
- --動態測試,用sqlstr字符串返回結果,用using關鍵詞傳遞參數
- open rc for sqlstr using intid;
- end if;
- return rc;
- end get;
- end pkg_test;
- /
自定義函數
用戶定義自定義函數像內置函數一樣返回標量值,也可以將結果集用表格變量返回
用戶自定義函數的類型:
標量函數:返回一個標量值
表格值函數{內聯表格值函數、多表格值函數}:返回行集(即返回多個值)
1、標量函數
Create function 函數名(參數)
Returns 返回值數據類型
[with {Encryption | Schemabinding }]
[as]
begin
SQL語句(必須有return 變量或值)
End
Schemabinding :將函數綁定到它引用的對象上(注:函數一旦綁定,則不能刪除、修改,除非刪除綁定)
Create function AvgResult(@scode varchar(10))
Returns real
As
Begin
Declare @avg real
Declare @code varchar(11)
Set @code=@scode + ‘%’
Select @avg=avg(result) from LearnResult_baijiali
Where scode like @code
Return @avg
End
執行用戶自定義函數
select 用戶名。函數名 as 字段別名
select dbo.AvgResult(‘s0002’) as result
用戶自定義函數返回值可放到局部變量中,用set ,select,exec賦值
declare @avg1 real ,@avg2 real ,@avg3 real
select @avg1= dbo.AvgResult(‘s0002’)
set @avg2= dbo.AvgResult(‘s0002’)
exec @avg3= dbo.AvgResult ‘s0002’
select @avg1 as avg1 ,@avg2 as avg2 ,@avg3 as avg3
函數引用
create function code(@scode varchar(10))
returns varchar(10)
as
begin
declare @ccode varchar(10)
set @scode = @scode + ‘%’
select @ccode=ccode from cmessage
where ccode like @scode
return @ccode
end
select name from class where ccode = dbo.code(‘c001’)
2、表格值函數
a、 內聯表格值函數
格式:
create function 函數名(參數)
returns table
[with {Encryption | Schemabinding }]
as
return(一條SQL語句)
create function tabcmess(@code varchar(10))
returns table
as
return(select ccode,scode from cmessage where ccode like @ccode)
b、 多句表格值函數
create function 函數名(參數)
returns 表格變量名table (表格變量定義)
[with {Encryption | Schemabinding }]
as
begin
SQL語句
end
多句表格值函數包含多條SQL語句,至少有一條在表格變量中填上數據值
表格變量格式
returns @變量名 table (column 定義| 約束定義 [,…])
對表格變量中的行可執行select,insert,update,delete , 但select into 和 insert 語句的結果集是從存儲過程插入。
Create function tabcmessalot (@code varchar(10))
Returns @ctable table(code varchar(10) null,cname varchar(100) null)
As
Begin
Insert @ctable
Select ccode,explain from cmessage
Where scode like @code
return
End
Select * from tabcmessalot(‘s0003’)