-
它是做什麼的?
- 作用:該語句可以方便地在PL/SQL程序中執行DML(insert,upate,delete,單列select),DDL(create,alter,drop),DCL(GRANT,REVOKE)語句;
- 語法:
EXECUTE IMMEDIATE 動態SQL字符串 [[BULK COLLECT] INTO
自定義變量,.....|
記錄類型] |
該語句由以下3個主要子句組成: |
注意:在使用USING或RETURNING語句時都可以設置參數模式(IN,OUT,IN OUT),其中對USING子句主要是使用變量定義的內容,所以默認的模式是IN模式。使用RETURNING子句時不需要設置內容,只需要接收返回內容,所以其模式爲OUT |
-
執行動態SQL
- 示例1:使用動態SQL創建表和PL/SQL塊
DECLARE v_sql_statement VARCHAR2(200) ; v_count NUMBER ; -- 保存查找結果 BEGIN SELECT COUNT(*) INTO v_count FROM user_tables WHERE table_name='MLDN_TAB' ; IF v_count = 0 THEN -- 數據表不存在 v_sql_statement := 'CREATE TABLE mldn_tab( id NUMBER PRIMARY KEY , url VARCHAR2(50) NOT NULL)' ; -- 定義動態SQL EXECUTE IMMEDIATE v_sql_statement ; ELSE -- 數據表存在 v_sql_statement := 'TRUNCATE TABLE mldn_tab' ; EXECUTE IMMEDIATE v_sql_statement ; END IF ; v_sql_statement := 'BEGIN FOR x IN 1 .. 10 LOOP INSERT INTO mldn_tab(id,url) VALUES (x , ''www .mldnjava.cn - '' || x) ; END LOOP ; END ;' ; EXECUTE IMMEDIATE v_sql_statement ; COMMIT ; -- 提交事務 END ; / |
本程序首先判斷要操作的數據表(mldn_tab)是否存在,如果數據表不存在,則使用動態SQL創建一張mldn_tab的數據表,如果存在,則執行TRUNCATE TABLE命令,將mldn_tab數據表中的數據清楚乾淨,之後繼續利用動態SQL執行一個PL/SQL程序塊,向mldn_tab數據表中增加10條記錄,運行流程圖如下: |
-
設置綁定變量
- 定義:使用動態SQL時,可以在定義SQL字符串裏採用佔位符的方式設置綁定變量,所設置的綁定變量需要在程序運行時動態地使用USING語句設置佔位符內容,而設置綁定變量的方式採用":佔位符名稱"的方式表示;
-
示例1:使用綁定變量
DECLARE v_sql_statement VARCHAR2(200) ; v_deptno dept.deptno%TYPE := 60 ; v_dname dept.dname%TYPE := 'MLDN' ; v_loc dept.loc%TYPE := '北京' ; BEGIN v_sql_statement := 'INSERT INTO dept(deptno,dname,loc) VALUES (:dno , :dna , :dl)' ; EXECUTE IMMEDIATE v_sql_statement USING v_deptno,v_dname,v_loc ; COMMIT ; END ; / |
此程序不能直接綁定NULL,需要通過變量設置。以下的設置是錯誤的: EXECUTE IMMEDIATE v_sql_statement USING v_deptno,v_dname,NULL; |
-
示例2:利用集合更新多條記錄
DECLARE v_sql_statement VARCHAR2(200) ; TYPE deptno_nested IS TABLE OF dept.deptno%TYPE NOT NULL ; TYPE dname_nested IS TABLE OF dept.dname%TYPE NOT NULL ; v_deptno deptno_nested := deptno_nested(10,20,30,40) ; v_dname dname_nested := dname_nested('財務部','研發部','銷售部','操作部') ; BEGIN v_sql_statement := 'UPDATE dept SET dname=:dna WHERE deptno=:dno' ; FOR x IN 1 .. v_deptno.COUNT LOOP EXECUTE IMMEDIATE v_sql_statement USING v_dname(x),v_deptno(x) ; END LOOP ; COMMIT ; END ; / |
本程序定義了兩個嵌套表類型(deptno_nested,dname_nested),然後將要更新的部門編號及部門名稱分別保存在這兩個嵌套表中,最後採用循環的方式利用 |
- 示例3:查詢數據
DECLARE v_sql_statement VARCHAR2(200) ; v_empno emp.empno%TYPE := 7369 ; v_emprow emp%ROWTYPE ; BEGIN v_sql_statement := 'SELECT * FROM emp WHERE empno=:eno' ; EXECUTE IMMEDIATE v_sql_statement INTO v_emprow USING v_empno ; DBMS_OUTPUT.put_line('僱員編號:' || v_emprow.empno || ',姓名:' || v_emprow.ename || ',職位:' || v_emprow.job) ; END ; / |
本程序在查詢語句中使用了綁定變量,由於此時需要返回結果,所以通過EXECUTE IMMEDIATE中的INTO子句將查詢結果保存在v_emprow變量中,同時還需要使用USING設置佔位符數據 |
-
示例4:上面的示例中,綁定變量的代碼都只是針對基本的數據類型,例如字符串,數字等。這種方式無法針對DDL操作;
-
在創建表時使用綁定變量
-
DECLARE v_sql_statement VARCHAR2(200) ; v_table_name VARCHAR2(200) := 'mldn' ; v_id_column VARCHAR2(200) := 'id' ; BEGIN v_sql_statement := 'CREATE TABLE :tn (:ci NUMBER PRIMARY KEY)' ; EXECUTE IMMEDIATE v_sql_statement USING v_table_name,v_id_column ; END ; / |
運行結果: 錯誤報告: ORA-00903: 表名無效 ORA-06512: 在 line 7 |
-
此時CREATE是DDL操作命令,無法使用綁定變量設置表名稱,同理,對於刪除表,截斷表操作也一樣無法使用,如果要使用,可以採用拼接字符串的方式完成,例如:
- 正確的代碼
DECLARE v_sql_statement VARCHAR2(200) ; v_table_name VARCHAR2(200) := 'mldn' ; v_id_column VARCHAR2(200) := 'id' ; BEGIN v_sql_statement := 'CREATE TABLE ' || v_table_name ||' (' || v_id_column ||' NUMBER PRIMARY KEY)' ; EXECUTE IMMEDIATE v_sql_statement ; END ; / |
-
接收DML更新行數
- 當用戶使用DML更新操作後,可以利用RETURNING INTO子句接收更新後被影響的數據行的詳細信息;
-
示例1:更新數據,取得更新後的結果
DECLARE v_sql_statement VARCHAR2(200) ; -- 定義SQL操作語句 v_empno emp.empno%TYPE := 7369 ; -- 要更新的僱員編號 v_salary emp.sal%TYPE ; -- 保存更新後的sal內容 v_job emp.job%TYPE ; -- 保存更新後的job內容 BEGIN v_sql_statement := 'UPDATE emp SET sal=sal*1.2,job=''開發'' ' || ' WHERE empno=:eno RETURNING sal,job INTO :salary,:job' ; EXECUTE IMMEDIATE v_sql_statement USING v_empno RETURNING INTO v_salary,v_job ; DBMS_OUTPUT.put_line('調整後的工資:' || v_salary || ',新的職位:' || v_job) ; END ; / |
提示:也可以使用RETURN接收影響數據行的數據,代碼片段如下: v_sql_statement := 'UPDATE emp SET sal=sal*1.2,job=''開發'' ' || ' WHERE empno=:eno RETURN sal,job INTO :salary,:job' ; EXECUTE IMMEDIATE v_sql_statement USING v_empno RETURN INTO v_salary,v_job ; |
-
示例2:刪除數據,取得刪除前的結果
|
||
分析:在進行數據刪除時,SQL語句使用RETURNING INTO將要刪除僱員的姓名及職位賦值給兩個綁定變量(:name,:sal),然後在使用EXECUTE IMMEDIATE時通過RETURNING INTO將已刪除僱員的姓名及工資賦值給v_ename和v_sal這兩個變量 |
-
示例3:編寫部門增加過程
CREATE OR REPLACE PROCEDURE dept_insert_proc( p_deptno IN OUT dept.deptno%TYPE , -- 此處可以將p_deptno的內容回傳 p_dname dept.dname%TYPE, -- 默認爲IN模式 p_loc dept.loc%TYPE) AS -- 默認爲IN模式 BEGIN SELECT MAX(deptno) INTO p_deptno FROM dept ; -- 取得最大的deptno內容 p_deptno := p_deptno + 1 ; -- 讓最大值部門編號加1,此處不考慮超過2位數字情況 INSERT INTO dept(deptno,dname,loc) VALUES (p_deptno,p_dname,p_loc) ; END ; / |
本過程要傳遞3個參數,其中對於部門編號(p_deptno)採用了IN OUT模式,所以此值可以傳回到調用處,在過程裏首先將查詢最大的部門編號。之後依次部門編號爲基礎進行加1的增長,這樣就可以產生一個新的部門編號,並將此部門編號保存在dept表中 |
-
接上例:編寫PL/SQL塊,調用過程
DECLARE v_sql_statement VARCHAR2(200) ; v_deptno dept.deptno%TYPE ; v_dname dept.dname%TYPE := 'MLDN' ; v_loc dept.loc%TYPE := '北京' ; BEGIN v_sql_statement := 'BEGIN dept_insert_proc(:dno , :dna , :dl) ; END ;' ; -- 定義PL/SQL塊 EXECUTE IMMEDIATE v_sql_statement USING IN OUT v_deptno , IN v_dname , v_loc ; DBMS_OUTPUT.put_line('新增部門編號爲:' || v_deptno) ; END ; / |
此操作使用USING傳遞被綁定的參數,由於在dept_insert_proc()過程中的第一個參數採用了IN OUT模式,所以可以接收部門增加後的部門編號數據 |