1. 程式人生 > >dbms_sql ORA-29471: DBMS_SQL 訪問被拒絕

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".
我開啟遊標一次,但是在迴圈中關閉了它,所以第二次我試圖解析的時候遊標已經無效了。