動態sql,綁定變量和調優經驗分析

爲什麼在PL/SQL裏用動態SQL ?

1. 你想執行SQL數據定義語句(如CREATE),一個數據控制語句(如GRANT),或一個會話控制語句(如ALTER SESSION),但它們和INSERT、UPDATE和DELETE語句不同,是不能被直接包括在PL/SQL程序裏的。這時需要動態SQL。

2. 有時SQL語句在編譯的時候不能全部確定,動態SQL使你能夠在運行時動態地構建SQL語句,從而創建更通用、靈活的應用程序。

例如,你可能想要給一個SELECT語句創建不同搜索條件的WHERE字句。再比如,你事先並不知道需要查詢哪些列,甚至連這些列叫什麼都不清楚。

怎樣動態SQL?

使用EXECUTE IMMEDIATE語句去解析和運行動態SQL語句或者一個匿名PL/SQL語塊。

EXECUTE IMMEDIATE的主要參數是一個包含SQL語句的字符串。字符串中可以包含佔位符,就是在任意名稱前面加一個冒號,叫做綁定變量。對應綁定變量,在INTO, USING, 和RETURNING INTO子句中可以用定義好的變量去替代這些綁定變量。

使用綁定變量可以減少解析的時間。關於綁定變量的使用,可以看下面的例3.

例1:

DECLARE
 v_sSQL VARCHAR2(1024);
BEGIN
 EXECUTE IMMEDIATE 'CREATE TABLE BONUS (ID NUMBER, AMT NUMBER)';
 EXECUTE IMMEDIATE 'alter session set sql_trace = TRUE';
 EXECUTE IMMEDIATE 'alter session set tracefile_identifier = ''Demo''';
 EXECUTE IMMEDIATE 'INSERT INTO BONUS VALUES(1, 12345.67890)';
 EXECUTE IMMEDIATE 'INSERT INTO BONUS VALUES(2, 12345.67890)';
 EXECUTE IMMEDIATE 'UPDATE BONUS SET AMT = NULL WHERE ID = 1';
 EXECUTE IMMEDIATE 'UPDATE BONUS SET AMT = NULL WHERE ID = 2';
 EXECUTE IMMEDIATE 'alter session set sql_trace =FALSE';
 EXECUTE IMMEDIATE 'DROP TABLE BONUS';

例2:

BEGIN
 EXECUTE IMMEDIATE 'BEGIN DBMS_OUTPUT.PUT_LINE(''semicolons''); END;';
END;

注意如果是匿名塊,在句尾要加分號。

例3:

DECLARE
 v_sSQL     VARCHAR2(1024);
 v_nNull    NUMBER;
 v_sTabName VARCHAR2(1024);
 v_nNewAmt  NUMBER;
BEGIN
 EXECUTE IMMEDIATE 'alter session set tracefile_identifier = ''Demo2''';
 EXECUTE IMMEDIATE 'alter session set sql_trace = TRUE';
 EXECUTE IMMEDIATE 'CREATE TABLE BONUS (ID NUMBER, AMT NUMBER)';
 v_sSQL := 'INSERT INTO BONUS VALUES(:id, :amt)';
 EXECUTE IMMEDIATE v_sSQL
   USING 1, 12345.67890;
 EXECUTE IMMEDIATE v_sSQL
   USING 2, 98765.43210;
 FOR v_nID IN 1 .. 2 LOOP
   EXECUTE IMMEDIATE 'UPDATE BONUS SET AMT = :amt WHERE ID = :id RETURNING AMT INTO :new_amt'
     USING v_nNull, v_nID
     RETURNING INTO v_nNewAmt;
   dbms_output.put_line(nvl(v_nNewAmt, 0));
 END LOOP;
 EXECUTE IMMEDIATE 'DROP TABLE BONUS';
 EXECUTE IMMEDIATE 'alter session set sql_trace =FALSE';
END;

使用綁定變量注意點:

1. USING子句一般跟默認是IN的參數

2. RETURNING子句一般默認OUT參數。用RETURNING INTO則沒有指定參數的INOUT

3. 佔位符只能用在可以設置變量的地方,比如WHERE子句中,不能是數據庫對象,比如表名

4. 在USING子句後不能放NULL。如果綁定變量就是一個NULL值,可以使用未初始化的變量繞開這個限制

動態SQL中使用Bulk:

1. Bulk可以綁定參數和一組PL/SQL集合類型的值。

2. 集合類型可以是任何PL/SQL的集合類型,比如索引表、嵌套表、變長數組

3. 有三個子句後面支持Bulk關鍵字,EXECUTE IMMEDIATE、FETCH和FORALL

例4:

DECLARE

 TYPE EmpCurTyp IS REF CURSOR;

 TYPE NumListIS TABLE OF NUMBER;

 TYPE NameListIS TABLE OF VARCHAR2(25);

 emp_cv EmpCurTyp;

 empids NumList;

 enames NameList;

 sals NumList;

BEGIN

 OPEN emp_cv FOR 'SELECT employee_id,last_name FROM employees';

 FETCH emp_cv BULK COLLECT INTO empids, enames;

 CLOSE emp_cv;

 EXECUTE IMMEDIATE 'SELECT salary FROM employees' BULK COLLECT INTO sals;

END;

例5:

DECLARE

 TYPE NameList IS TABLE OF VARCHAR2(15);

 enames NameList;

 bonus_amt NUMBER := 50;

 sql_stmt VARCHAR(200);

BEGIN

 sql_stmt:= 'UPDATE employees SET salary = salary + :1 RETURNING last_name INTO :2';

 EXECUTE IMMEDIATE sql_stmt USING bonus_amt RETURNING BULK COLLECT INTO enames;

END;

例6:

DECLARE
 TYPE NumList IS TABLE OF NUMBER;
 TYPE NameList IS TABLE OF VARCHAR2(15);
 empids NumList;
 enames NameList;
BEGIN
 empids := NumList(101, 102, 103, 104, 105);
 FORALL i IN 1 .. 5
   EXECUTE IMMEDIATE 'UPDATE employees SET salary = salary * 1.04 WHERE employee_id= :1
                     RETURNING last_name INTO :2'
     USING empids(i)
     RETURNING BULK COLLECT
     INTO enames;
END;

動態SQL調優經驗分享

調優的時候可以使用tkprof。因爲調優前後SQL功能是一致的,所以主要關注遊標解析耗費的時間,所以在用tkprof時,加上sort=prsela,使輸出文件中SQL的順序按照遊標解析耗費的時間排序。

下面是8個調優案例,包括調優前和調優後的SQL

案例一:

第一個是能夠使用綁定變量的就多用,不要使用連接符直接拼在字符串中,那樣SQL每次運行都需要重新解析。

案例二:

待優化SQL:

BEGIN
 SELECT VALUE
   INTO v_sSymbolCond
   FROM default_values
  WHERE infeed_id = p_sInfeedId
    AND field = NVL2(v_nMsgType, to_char(v_nMsgType) || '/', '') ||
        'SYMBOL_CONDITION';
 v_sSymbolCond  := REPLACE(v_sSymbolCond,
                           'IDENTIFIER',
                           '''' || p_sIdentifier || '''');
 v_sRetrieveSql := 'SELECT ' || v_sSymbolCond || ' FROM dual';
 EXECUTE IMMEDIATE v_sRetrieveSql
   INTO v_sSymbol;
EXCEPTION
 WHEN NO_DATA_FOUND THEN
   v_sSymbol := p_sIdentifier;
END;

優化後SQL:

BEGIN
 SELECT VALUE
   INTO v_sSymbolCond
   FROM default_values
  WHERE infeed_id = p_sInfeedId
    AND field = NVL2(v_nMsgType, to_char(v_nMsgType) || '/', '') ||
        'SYMBOL_CONDITION';
 v_sSymbolCond  := REPLACE(v_sSymbolCond, 'IDENTIFIER', ':p_sIdentifier');
 v_sRetrieveSql := 'BEGIN SELECT ' || v_sSymbolCond ||
                   ' INTO :v_sSymbol FROM dual; END;';
 EXECUTE IMMEDIATE v_sRetrieveSql
   USING p_sIdentifier, OUT v_sSymbol;
EXCEPTION
 WHEN NO_DATA_FOUND THEN
   v_sSymbol := p_sIdentifier;
END;

案例三:

案例四:

案例五:

案例六:

案例七:

案例八:

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