一、靜態(tài)SQL和動態(tài)SQL的概念。
1、靜態(tài)SQL
靜態(tài)SQL是我們常用的使用SQL語句的方式,就是編寫PL/SQL時,SQL語句已經(jīng)編寫好了。因為靜態(tài)SQL是在編寫程序時就確定了,我們只能使用SQL中的DML和事務控制語句,但是DDL語句,以及會話控制語句卻不能再PL/SQL中直接使用,如動態(tài)創(chuàng)建表或者某個不確定的操作時,這就需要動態(tài)SQL來實現(xiàn)。
2、 動態(tài)SQL
動態(tài)SQL是指在PL/SQL編譯時SQL語句是不確定的,如根據(jù)用戶輸入的參數(shù)的不同來執(zhí)行不同的操作。編譯程序對動態(tài)語句部分不進行處理,只是在程序運行時動態(tài)創(chuàng)建語句,對語句進行分析,病執(zhí)行該語句。
靜態(tài)SQL的優(yōu)勢是性能較高,但不靈活。動態(tài)SQL的優(yōu)勢是靈活,缺點是性能稍差。
?
二、動態(tài)創(chuàng)建DML、DDL的SQL語句。
動態(tài)創(chuàng)建SQL有一下幾類:
1、DDL語句、DCL語句、非查詢的DML語句、單行查詢的SELECT語句,這類可以使用EXECUTE IMMEDIATE語句執(zhí)行。
2、多行查詢的SELECT語句可以使用游標來實現(xiàn)。
3、通過DBMS_SQL程序包實現(xiàn)。
下面來介紹以上3種情況:
?
1、使用EXECUTE IMMEDIATE語句處理相關語句:
語法:
EXECUTE IMMEDIATE dynamic_sql_string
[into define_variable_list]
[using bind_argument_list];
例:
動態(tài)創(chuàng)建表t1
-- 處理DDL、DCL語句,根據(jù)用戶輸入的表明及字段名動態(tài)創(chuàng)建表t1 DECLARE tablename VARCHAR2 ( 20 ); -- 表名 field1 VARCHAR2 ( 20 ); -- 字段1名稱 datatype1 VARCHAR2 ( 20 ); -- 字段1類型 field2 VARCHAR2 ( 20 ); -- 字段2名稱 datatype2 VARCHAR2 ( 20 ); -- 字段2類型 str_sql VARCHAR2 ( 500 ); -- 拼接SQL語句的字符串 BEGIN tablename : = ' t1 ' ; field1: = ' id ' ; datatype1: = ' number ' ; field2: = ' name ' ; datatype2: = ' varchar(20) ' ; str_sql : = ' create table ' || tablename || ' ( ' || field1 || ' ' || datatype1 || ' , ' || field2 || ' ' || datatype2 || ' ) ' ; EXECUTE IMMEDIATE str_sql; EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.put_line( ' 操作失敗! ' ); END ;
?
動態(tài)插入數(shù)據(jù);
-- 動態(tài)處理費查詢的DML語句:向剛才創(chuàng)建的表中插入數(shù)據(jù) DECLARE v_id NUMBER ; -- 輸入序號; v_name VARCHAR ( 20 ); -- 輸入姓名; str_sql VARCHAR2 ( 500 ); -- 保存拼接的SQL語句 BEGIN v_id : = & vid; v_name : = ' &name ' ; str_sql : = ' insert into t1 values(:1,:2) ' ; -- 使用占位符代表變量 EXECUTE IMMEDIATE str_sql USING v_id,v_name; -- 使用變量替換SQL中的占位符,v_id替換:1,v_name替換:2,依此類推。 COMMIT ; -- 執(zhí)行完畢后直接提交 END ;
?
查詢表中的數(shù)據(jù)有多少行
-- 處理單行查詢的SELECT舉例,查詢表中的數(shù)據(jù)有多少行 DECLARE v_count NUMBER ; str_sql VARCHAR2 ( 500 ); BEGIN str_sql : = ' select count(*) from t1 ' ; EXECUTE IMMEDIATE str_sql INTO v_count; -- 將查詢的結果存放到變量v_count中。 DBMS_OUTPUT.put_line(v_count); END ;
?
綁定變量的優(yōu)缺點:
1)可以再庫緩存中共享游標,節(jié)省了CPU等資源,可以避免額外開銷。
2)SQL語句使用綁定變量可以避免被注入攻擊。
3)綁定變量是一種減少應用程序在分析查詢時使用栓鎖數(shù)目的可靠方法。
不適合使用變量綁定的情況:
1)對于隔相當長一段時間才執(zhí)行一次的SQL語句,利用綁定變量的好處hi被不能有效利用而抵消。
2)在數(shù)據(jù)倉庫的情況下。
3)在對建有索引的字段,且字段非常大時,利用綁定變量可能會導致查詢計劃錯誤,從而導致查詢效率非常低。
?
實現(xiàn)DDL語句中的注意事項:
PL/SQL塊使用動態(tài)SQL執(zhí)行DDL語句的時候與其它不同,在DDL中不能使用綁定變量。
?
實現(xiàn)DML語句中的注意事項:
不能使用綁定變量替換實際的數(shù)據(jù)庫對象名(表,視圖,列等),只能替換字面兩,如果對象名在運行時生成的,我們只能使用字符串拼接。
?
?
2、通過游標實現(xiàn)多行查詢的SELECT語句
REF游標可以處理返回屆國際的動態(tài)SQL。實現(xiàn)動態(tài)SQL的REF游標聲明和普通REF游標相同,知識OPEN時綁定的是動態(tài)SQL字符串。
例:查詢emp表中所有的數(shù)據(jù)。
DECLARE TYPE ref_cur IS REF CURSOR ; rc ref_cur; emprow emp % ROWTYPE; v_sql VARCHAR2 ( 100 ): = ' select * from emp where deptno = :x ' ; -- 動態(tài)執(zhí)行的SQL語句 BEGIN OPEN rc FOR v_sql USING 30 ; -- 打開游標,綁定執(zhí)行的SQL語句,并傳遞參數(shù) LOOP FETCH rc INTO emprow; EXIT WHEN rc % NOTFOUND; dbms_output.put_line( ' name: ' || emprow.ename || ' sal: ' || emprow.sal); END LOOP; CLOSE rc; END ;
?
3、DBMS_SQL程序包
DBMS_SQL程序包是系統(tǒng)提供給我們的另一種使用動態(tài)SQL的方法。程序包中封裝了一些列存儲過程,幫助我們動態(tài)執(zhí)行SQL。
使用DBMS_SQL包實現(xiàn)動態(tài)SQL的步驟如下:
1)將要執(zhí)行的SQL語句或一個語句塊放到一個字符串變量中。
2)使用DBMS_SQL包的parse過程來分析該字符串。
3)使用DBMS_SQL包的bind_variable過程來綁定變量。
4)使用DBMS_SQL包的execute函數(shù)來執(zhí)行語句。
例:使用DBMS_SQL創(chuàng)建表
DECLARE tablename VARCHAR2 ( 20 ) : = ' t2 ' ; -- 表名 field1 VARCHAR2 ( 20 ) : = ' id ' ; -- 字段1名稱 datatype1 VARCHAR2 ( 20 ) : = ' number ' ; -- 字段1類型 field2 VARCHAR2 ( 20 ) : = ' name ' ; -- 字段2名稱 datatype2 VARCHAR2 ( 20 ) : = ' varchar(20) ' ; -- 字段2類型 v_sql VARCHAR2 ( 500 ) : = ' create table ' || tablename || ' ( ' || field1 || ' ' || datatype1 || ' , ' || field2 || ' ' || datatype2 || ' ) ' ; -- 拼接SQL語句的字符串 v_cursor NUMBER ; -- 定義光標 v_row NUMBER ; -- 行數(shù) BEGIN v_cursor: = dbms_sql.open_cursor; -- 為處理打開光標 dbms_sql.parse(v_cursor,v_sql,dbms_sql.native); -- 分析語句; v_row: = DBMS_SQL. execute (v_cursor); -- 執(zhí)行sql語句; dbms_sql.close_cursor(v_cursor); -- 關閉光標; DBMS_OUTPUT.put_line(v_row); END ;
向表中插入一條數(shù)據(jù):
DECLARE v_id NUMBER : = & vid; v_name VARCHAR2 ( 20 ) : = ' &vname ' ; v_sql VARCHAR2 ( 100 ) : = ' insert into t2 values(:id,:name) ' ; v_cursor NUMBER ; v_row NUMBER ; BEGIN v_cursor: = dbms_sql.open_cursor; dbms_sql.parse(v_cursor,v_sql,dbms_sql.native); dbms_sql.bind_variable(v_cursor, ' :id ' ,v_id); dbms_sql.bind_variable(v_cursor, ' :name ' ,v_name); v_row : = dbms_sql. execute (v_cursor); dbms_sql.close_cursor(v_cursor); COMMIT ; DBMS_OUTPUT.put_line(v_row); END ;
?
查詢EMP中的數(shù)據(jù)
DECLARE V_DEPTNO NUMBER : = & DEPTNO; V_SQL VARCHAR2 ( 100 ) : = ' select empno,ename,sal from emp where deptno = :deptno ' ; V_CURSOR NUMBER ; V_NO NUMBER ; V_ENAME VARCHAR2 ( 20 ); V_SAL NUMBER ; v_start NUMBER ; BEGIN V_CURSOR : = DBMS_SQL.OPEN_CURSOR; -- 打開游標 DBMS_SQL.PARSE(V_CURSOR, V_SQL, DBMS_SQL.NATIVE); -- 解析動態(tài)SQL語句 DBMS_SQL.BIND_VARIABLE(V_CURSOR, ' :deptno ' , V_DEPTNO); -- 傳遞參數(shù) DBMS_SQL.DEFINE_COLUMN(V_CURSOR, 1 , V_NO); -- 定義輸出的列,和查詢的列相匹配 DBMS_SQL.DEFINE_COLUMN(V_CURSOR, 2 , V_ENAME, 20 ); DBMS_SQL.DEFINE_COLUMN(V_CURSOR, 3 , V_SAL); v_start : = DBMS_SQL. execute (V_CURSOR); -- 執(zhí)行SQL語句,需要有接受返回值 LOOP EXIT WHEN DBMS_SQL.FETCH_ROWS(V_CURSOR) <= 0 ; -- 解析游標, DBMS_SQL.COLUMN_VALUE(V_CURSOR, 1 , V_NO); -- 將當前行的數(shù)據(jù)寫入上面對應的列中。 DBMS_SQL.COLUMN_VALUE(V_CURSOR, 2 , V_ENAME); DBMS_SQL.COLUMN_VALUE(V_CURSOR, 1 , V_SAL); DBMS_OUTPUT.PUT_LINE( ' no: ' || V_NO || ' enmae: ' || V_ENAME || ' sal: ' || V_SAL); -- 輸出內(nèi)容 END LOOP; dbms_sql.close_cursor(v_cursor); -- 關閉游標 END ;
?
更多文章、技術交流、商務合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
