如何使用動態SQL語句?

這裏只介紹動態SQL的使用。關於動態SQL語句的語法,參見:http://blog.csdn.net/chiclewu/article/details/16097133

1.什麼是時候需要使用動態SQL?

  • SQL文本在編譯時是未知的。

例如,SELECT語句包含的標識符(如表名)在編譯時是未知的,或者WHERE子句的條件數量在編譯時是未知。

  • 靜態SQL不支持

例如,在PL/SQL中用靜態SQL只能執行查詢以及DML語句。如果想要執行DDL語句,只能使用動態SQL。

當讓使用靜態SQL,也有它的好處:

  • 編譯成功驗證了靜態SQL語句引用有效的數據庫對象和訪問這些對象的權限

  • 編譯成功創建了模式對象的依賴關係

2.EXECUTE IMMEDIATE語句

EXECUTE IMMEDIATE語句的意思是使用本地動態SQL處理大多數動態SQL語句。

如果動態SQL語句是自包含的(也就是說,它的綁定參數沒有佔位符,並且結果不可能返回錯誤),則EXECUTE IMMEDIATE語句不需要子句。

如果動態SQL語句包行佔位符綁定參數,每個佔位符在EXECUTE IMMEDIATE語句的子句中必須有一個相應的綁定參數,具體如下:

  • 如果動態SQL語句是一個最多隻能返回一行的SELECT語句,OUT綁定參數放置在INTO子句,IN綁定參數放置在USING子句。
  • 如果動態SQL語句是一個可以返回多行的SELECT語句,OUT綁定參數放置在BULK COLLECT INTO子句,IN綁定參數放置在USING子句。
  • 如果動態SQL語句是一個除了SELECT以外的其他DML語句,且沒有RETURNING INTO子句,所有的綁定參數放置在USING子句中。
  • 如果動態SQL還語句一個匿名PL/SQL塊或CALL語句,把所有的綁定參數放置在USING子句中。

如果動態SQL語句調用一個子程序,請確保:

  • 每個對應子程序參數佔位符的綁定參數與子程序參數具有相同的參數模式和兼容的數據類型。

  • 綁定參數不要有SQL不支持的數據類型(例如,布爾類型,關聯數組,以及用戶自定的記錄類型)

     

USING子句不能包含NULL字面量。如果想要在USING子句中使用NULL值,可以使用位初始化的變量或者函數顯示將NULL轉換成一個有類型的值。

 

2.1動態SQL語句是一個最多隻能返回一行的SELECT語句

使用動態SQL語句返回單列,查詢SCOTT的薪水:

declare
  v_sql_text varchar2(1000);
  v_sal      number;
  v_ename    emp.ename%type := 'SCOTT';
begin
  v_sql_text := 'select e.sal from emp e where e.ename = :ename';

  execute immediate v_sql_text
    into v_sal
    using v_ename;

  dbms_output.put_line(v_ename || ':' || v_sal);

end;

 

使用動態SQL返回一條記錄,查詢SCOTT的基本信息:

declare
  v_sql_text varchar2(1000);
  v_ename    emp.ename%type := 'SCOTT';
  vrt_emp    emp%rowtype;
begin
  v_sql_text := 'select * from emp e where e.ename = :ename';
  execute immediate v_sql_text
    into vrt_emp
    using v_ename;
  dbms_output.put_line(v_ename || '的基本信息:');
  dbms_output.put_line('工號:' || vrt_emp.empno);
  dbms_output.put_line('工資:' || vrt_emp.sal);
  dbms_output.put_line('入職日期:' || vrt_emp.hiredate);

end;

 

2.2動態SQL語句是一個可以返回多行的SELECT語句

2.2.1只有一個佔位符

使用動態SQL語句返回多行記錄,查詢30部門的員工基本信息:

declare
  v_sql_text varchar2(1000);
  v_deptno   emp.deptno%type := 30;

  type nt_emp is table of emp%rowtype;
  vnt_emp nt_emp;
begin
  v_sql_text := 'select * from emp e where e.deptno = :deptno';
  execute immediate v_sql_text bulk collect
    into vnt_emp
    using v_deptno;

  for i in 1 .. vnt_emp.count loop
    dbms_output.put_line(vnt_emp(i).ename || '的基本信息:');
    dbms_output.put_line('工號:' || vnt_emp(i).empno);
    dbms_output.put_line('工資:' || vnt_emp(i).sal);
    dbms_output.put_line('入職日期:' || vnt_emp(i).hiredate);
    dbms_output.put_line('');
  end loop;

end

 

2.2.2多個佔位符

查詢20部門工資大於2000的員工基本信息:

declare
  v_sql_text varchar2(1000);
  v_deptno   emp.deptno%type := 20;
  v_sal      number := 2000;

  type nt_emp is table of emp%rowtype;
  vnt_emp nt_emp;
begin
  v_sql_text := 'select * from emp e where e.sal>:sal and e.deptno = :deptno';
  execute immediate v_sql_text bulk collect
    into vnt_emp
    using v_sal, v_deptno; --注意綁定多個變量時,綁定變量只與佔位符位置有關,與佔位符名稱無關,

  for i in 1 .. vnt_emp.count loop
    dbms_output.put_line(vnt_emp(i).ename || '的基本信息:');
    dbms_output.put_line('工號:' || vnt_emp(i).empno);
    dbms_output.put_line('工資:' || vnt_emp(i).sal);
    dbms_output.put_line('入職日期:' || vnt_emp(i).hiredate);
    dbms_output.put_line('');
  end loop;

注意:對於SQL文本,佔位符名稱是沒有意義的,綁定變量與佔位符名稱無關,只與佔位符的配置有關。即使有多個相同名稱佔位符,也需要每個佔位符對應一個綁定變量。對於PL/SQL塊,佔位符名稱是有意義的,相同名稱的佔位符,只需要第一個佔位符綁定變量。

 

2.3動態SQL語句是一個帶有RETURNING子句的DML語句

KING的工資增長20%,返回增長後的工資:

eclare
  v_sql_text varchar2(1000);
  v_sal      number;
  v_ename    emp.ename%type := 'KING';
begin

  v_sql_text := 'update emp e  set e.sal= e.sal*1.2 where e.ename = :ename returning e.sal into :sal';

  execute immediate v_sql_text
    using v_ename
    returning into v_sal;

  dbms_output.put_line(v_ename || ':' || v_sal);

end;

 

注意:只有當v_sql_text語句有returning into子句時,動態SQL語句才能使用returning into子句。

 

2.4給佔位符傳遞NULL值

2.4.1通過未初始化變量傳遞NULL值

declare
  v_sql_text varchar2(1000);
  v_deptno   emp.ename%type := 'ALLEN';
  v_comm     emp.comm%type;

begin
  v_sql_text := 'update emp e set e.comm = :comm where e.ename =:ename';
  execute immediate v_sql_text
    using v_comm, v_deptno;
end;

2.4.2通過函數將NULL值顯式的轉換成一個有類型的值

declare
  v_sql_text varchar2(1000);
  v_deptno   emp.ename%type := 'ALLEN';
begin
  v_sql_text := 'update emp e set e.comm = :comm where e.ename =:ename';
  execute immediate v_sql_text
    using to_number(null), v_deptno;
end;

 

3.OPEN FOR語句

PL/SQL引入OPEN FOR語句實際上並不是爲了支持本地動態SQL,而是爲了支持遊標變量。現在它以一種極其優雅的方式實現了多行的動態查詢。

  1. 使用OPEN FOR語句來關聯動態SQL語句的遊標變量,在OPEN FOR語句的USING子句中,指定動態SQL語句每個佔位符的綁定參數。

  2. 使用FETCH語句獲取運行時結果集。
  3. 使用CLOSE語句關閉遊標變量

使用OPEN FOR語句查詢出10部門的員工的基本信息:

declare
  type rc_emp is ref cursor;
  vrc_emp rc_emp;

  v_sql_text varchar2(1000);
  v_deptno   emp.deptno%type := 10;
  vrt_emp    emp%rowtype;

begin
  v_sql_text := 'select * from emp e where e.deptno=:deptno';

  open vrc_emp for v_sql_text
    using v_deptno;
  loop
    exit when vrc_emp%notfound;
    fetch vrc_emp
      into vrt_emp;
 
    dbms_output.put_line(vrt_emp.ename || '的基本信息:');
    dbms_output.put_line('工號:' || vrt_emp.empno);
    dbms_output.put_line('工資:' || vrt_emp.sal);
    dbms_output.put_line('入職日期:' || vrt_emp.hiredate);
    dbms_output.put_line('');
 
  end loop;
  close vrc_emp;

end;

 

4.重複的佔位符名稱

如果在動態SQL語句重複佔位符名稱,要知道佔位符關聯綁定參數的方式依賴於動態語句的類型。

  • 如果執行的是一個動態SQL字符串,則必須爲每一個佔位符提供一個綁定參數,即使這些佔位符是重複的。
  • 如果執行的是一個動態PL/SQL塊,則必須爲每一個唯一佔位符提供一個綁定參數,即重複的佔位符只需要提供一個綁定參數。

4.1重複佔位符的動態SQL字符串

declare
  v_sql_text varchar2(1000);
  v_sal      emp.sal%type := 4000;
  v_comm     emp.comm%type;
  v_ename    emp.ename%type := 'SCOTT';
begin

  v_sql_text := 'update emp e set e.sal=:sal , e.comm = :sal*0.1  where e.ename =:ename returning e.comm into :comm ';

  execute immediate v_sql_text
    using v_sal, v_sal, in v_ename
    returning into v_comm;
  dbms_output.put_line(v_ename || '分紅:' || v_comm);

end;

 

4.2重複佔位符的動態PL/SQL塊

declare
  v_sql_text varchar2(1000);
  v_sal      number;
  v_ename    emp.ename%type := 'KING';
begin

  v_sql_text := ' begin select e.sal,e.ename into :sal,:ename from emp e where e.ename =:ename; end;';

  execute immediate v_sql_text
    using out v_sal, in out v_ename;
  dbms_output.put_line(v_ename || ':' || v_sal);

end;

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章