爲什麼在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;
案例三:
案例四:
案例五:
案例六:
案例七:
案例八: