dbms_sql ORA-29471: DBMS_SQL 訪問被拒絕
come from : http://www.itpub.net/thread-1939425-1-1.html
thanks the writer.
原始出處:
http://www.plsqlchallenge.com/
作者:Steven Feuerstein
執行環境:SQLPLUS, SERVEROUTPUT已開啟
我執行了下列語句:
CREATE TABLE plch_greens
(
id INTEGER PRIMARY KEY,
nm VARCHAR2 (100) UNIQUE
)
/
BEGIN
INSERT INTO plch_greens
VALUES (1, 'Broccoli');
INSERT INTO plch_greens
VALUES (2, 'Kale');
COMMIT;
END;
/
哪些選項在執行之後會導致下列文字被輸出?
Updated Broccoli
Updated Kale
注:每個選項的迴圈體內部的程式碼大部分是相同的,請看註釋說明。
(A)
DECLARE
l_cursor INTEGER := DBMS_SQL.open_cursor;
l_feedback INTEGER;
BEGIN
FOR rec IN ( SELECT *
FROM plch_greens
ORDER BY id)
LOOP
/* 相同程式碼起始點... */
DBMS_SQL.parse (
l_cursor,
'update plch_greens set nm = upper (nm) where id = :my_id',
DBMS_SQL.native);
DBMS_SQL.bind_variable (l_cursor, 'my_id', rec.id);
DBMS_OUTPUT.put_line ('Updated ' || rec.nm);
l_feedback := DBMS_SQL.execute (l_cursor);
/* ...相同程式碼結束點 */
END LOOP;
DBMS_SQL.close_cursor (l_cursor);
END;
/
A: 我開啟遊標一次,在迴圈中重複使用它,在迴圈結束後關閉了它。沒有必要反覆開啟關閉,正如這個邏輯中所體現的。
然而它還有改善的餘地。既然SQL語句本身沒有改變(只有繫結變數改變了),對PARSE的呼叫也只需要在迴圈外執行一次,然後只需繫結一個新值。
當然,對於這麼簡單的動態SQL場景,我們還可以更進一步,把DBMS_SQL改成EXECUTE IMMEDIATE。
B)
DECLARE
l_cursor INTEGER;
l_feedback INTEGER;
BEGIN
FOR rec IN ( SELECT *
FROM plch_greens
ORDER BY id)
LOOP
l_cursor := DBMS_SQL.open_cursor;
/* 相同程式碼起始點... */
DBMS_SQL.parse (
l_cursor,
'update plch_greens set nm = upper (nm) where id = :my_id',
DBMS_SQL.native);
DBMS_SQL.bind_variable (l_cursor, 'my_id', rec.id);
DBMS_OUTPUT.put_line ('Updated ' || rec.nm);
l_feedback := DBMS_SQL.execute (l_cursor);
/* ...相同程式碼結束點 */
DBMS_SQL.close_cursor (l_cursor);
END LOOP;
END;
/
沒有發生錯誤,顯示的資訊也正確,但是我做了很多不必要的事情,在迴圈體中不斷開啟和關閉遊標。
(C)
DECLARE
l_cursor INTEGER;
l_feedback INTEGER;
BEGIN
FOR rec IN ( SELECT *
FROM plch_greens
ORDER BY id)
LOOP
l_cursor := DBMS_SQL.open_cursor;
/* 相同程式碼起始點... */
DBMS_SQL.parse (
l_cursor,
'update plch_greens set nm = upper (nm) where id = :my_id',
DBMS_SQL.native);
DBMS_SQL.bind_variable (l_cursor, 'my_id', rec.id);
DBMS_OUTPUT.put_line ('Updated ' || rec.nm);
l_feedback := DBMS_SQL.execute (l_cursor);
/* ...相同程式碼結束點 */
END LOOP;
END;
/
仍然可以看到正確結果,但是更加糟糕了。我不斷開啟新遊標,但是沒有關閉,這意味著它會在我的會話中保持開啟狀態,直到會話結束為止。
(D)
DECLARE
l_cursor INTEGER := DBMS_SQL.open_cursor;
l_feedback INTEGER;
BEGIN
FOR rec IN ( SELECT *
FROM plch_greens
ORDER BY id)
LOOP
/* 相同程式碼起始點... */
DBMS_SQL.parse (
l_cursor,
'update plch_greens set nm = upper (nm) where id = :my_id',
DBMS_SQL.native);
DBMS_SQL.bind_variable (l_cursor, 'my_id', rec.id);
DBMS_OUTPUT.put_line ('Updated ' || rec.nm);
l_feedback := DBMS_SQL.execute (l_cursor);
/* ...相同程式碼結束點 */
DBMS_SQL.close_cursor (l_cursor);
END LOOP;
END;
D: 這個選項會報錯:
"ORA-29471: DBMS_SQL access denied".
我開啟遊標一次,但是在迴圈中關閉了它,所以第二次我試圖解析的時候遊標已經無效了。