有關如何調試動態SQL代碼的6個提示

動態SQL代碼可能很難調試。舉個例子,讓我們來看看如何應對這一挑戰的6個技巧。

MariaDB的TX   成熟的生產和由社區驅動,爲任何和每一個企業一個完整的數據庫解決方案-爲現代應用的現代數據庫。

通過我們的AskTOM PL / SQL辦公時間  計劃得到了這個請求  

親愛的專家,我在下面寫了以下代碼:
----------------------------------------------
       宣佈
       v_Table all_tables .TABLE_NAME%type;
       v_Mnt varchar2(2):= '08' ;
       類型Cur_type 是 Ref Cursor;
        C Cur_type;

       開始
               v_Table:= 'ddi_ticket_10_1018' ;
               打開C表示“SELECT * from bill”。|| v_Table || v_Mnt || 'where called_nbr = 123' ;

               結束;
-------------------------------------------------- -----------------
執行此代碼時,我面臨此錯誤消息。
ORA- 00933 -SQL命令未正確結束
ORA- 06512:第9行。
請檢查上面的代碼並修改語法更正

我一眼就能猜到問題是什麼。

你可以嗎?

我不是在吹噓。我只是鼓勵你  不要  進一步閱讀,而是檢查代碼。什麼可能導致他的問題?

動態SQL可能很棘手 - 在OPEN-FOR或EXECUTE IMMEDIATE是PL / SQL語言的複雜部分之前並非如此。但是因爲它很容易搞亂你動態構建的SQL或PL / SQL。你可以:

  • 遺漏“;” (來自PL / SQL代碼)。

  • 忘記在SQL的各個部分之間留下空格。

  • 有無與倫比的括號。

  • 一直打開。

在這種情況下,我回信說:“我很確定你會發現問題是你在”Where“關鍵字之前沒有空格:

v_Mnt || 'where called_nbr = 123' ;

然後,這個交換提醒我,我應該寫一篇文章,其中包含一些簡單的技巧,可以讓您更輕鬆地調試動態SQL代碼並確保它按預期工作。開始。

  1. 使用AUTHID CURRENT_USER(調用者權限定義子程序

  2. 如果您正在執行動態  DDL,請使子程序成爲  自治事務

  3. 始終執行IMMEDIATE或OPEN FOR變量。

  4. 始終處理動態SQL執行可能引起的異常。

  5. 記錄錯誤信息  以及 您嘗試執行的變量。

  6. 在子程序中構建測試模式。

我將通過一個超級有用+危險程序的版本開始來證明這些點的價值,這些程序忽略了所有這些:drop_whatever程序。

PROCEDURE drop_whatever(nm   IN VARCHAR2 DEFAULT '%',
                         典型的IN VARCHAR2 DEFAULT '%')
IS
   CURSOR object_cur
   IS
      SELECT object_name,object_type
        FROM user_objects
       WHERE      object_name LIKE UPPER(nm)
             AND object_type LIKE UPPER(典型值)
             AND object_name <> 'DROP_WHATEVER' ;
開始
   FOR rec IN object_cur
   循環
      執行立即
            'DROP'
         || rec .object_type
         || ''
         || rec .object_name
         || 案件
               當rec .object_type  IN('TABLE','OBJECT')時
               然後
                  'CASCADE CONSTRAINTS'
               其他
                  空值
            結束;
   結束循環;
結束;

在這個過程中,我使用靜態遊標來查找所有匹配的對象,然後對於找到的每個對象,我執行動態DDL DROP語句。

它很有用,因爲我可以通過輸入任何內容來刪除模式中的所有數據庫對象

執行drop_whatever()

而且出於同樣的原因它也很危險  

哦,等等。鑑於它有多麼有用,也許我們應該讓  每個人都  能夠使用它。我知道,我會運行這個命令:

GRANT EXECUTE ON drop_whatever TO PUBLIC

嘿,怎麼可能出錯?:-)

非常非常。讓我們逐步完善我的建議並突出潛在的問題。

1.使用AUTHID CURRENT_USER(調用者權限定義子程序

該過程沒有AUTHID子句(我打賭你的大多數存儲的程序單元都沒有)。這意味着它默認爲“定義權限”。這意味着它始終以過程的定義者/所有者的特權執行。

這意味着,如果,例如,HR擁有drop_whatever然後SCOTT執行它(謝謝你,GRANT到PUBLIC!),如:

EXEC HR .drop_whatever()

那麼SCOTT將剛剛刪除HR模式中的所有數據庫對象  

2.如果您正在執行動態  DDL,請使子程序成爲  自治事務

關於DDL語句的事情是Oracle在執行 DDL語句之前和之後都執行  隱式提交因此,如果您有一個執行動態DDL的存儲過程,您必須警告可能使用它的每個人,他們的會話中的任何未完成的更改(這只是粗魯)或您將此語句添加到您的過程:

PRAGMA AUTONOMOUS_TRANSACTION;

現在,在程序執行的任何提交(或回滾)將僅影響所做的更改  中 的程序。

3.始終執行IMMEDIATE或OPEN FOR變量。

這是一件非常簡單的事情,但是當你試圖找出你的程序有什麼問題時,它可以節省你很多時間。

事情就是這樣:不難想出如何使用EXECUTE IMMEDIATE。但是 在運行時正確構造字符串可能  非常棘手。如此多的小錯誤都可能導致錯誤。如果直接在EXECUTE IMMEDIATE語句中構造字符串,那麼如何查看執行的內容以及可能出錯的位置?

例如,假設在drop_whatever過程中,我構造了我的DROP語句,如下所示:

執行立即
   'DROP'
|| rec .object_type
|| rec .object_name ...

當我試圖放下桌子時,我看到:

ORA - 00950:無效的 DROP  選項

這告訴我什麼?不多。它認爲我認爲哪個選項無效?我剛剛嘗試做什麼?

另一方面,如果我將我希望執行的表達式分配給變量,然後調用EXECUTE IMMEDIATE,我可以捕獲錯誤並記錄/顯示該變量(請參閱下面的drop_whatever的第二個實現)。然後我可能會看到類似的東西:

DROP  SYNONYMV $ SQL  -  失敗

哦! 我現在明白了。我沒有在對象類型和對象名稱之間包含空格。傻我。因此,總是聲明一個變量,將動態構造的SQL語句分配給該變量,然後執行它立即執行。

4.始終處理動態SQL執行可能引起的異常。

5.記錄錯誤信息  以及 您嘗試執行的變量。

如果未捕獲異常,則無法記錄或顯示該變量。如果您不保留該變量值,則很難向您的支持團隊提供有用的問題報告。

除了嘲笑代碼的糟糕設計之外,你做不了什麼。

6.在子程序中構建測試模式。

我一直在編寫代碼並且長時間搞砸了代碼,我已經瞭解到它非常有用 - 尤其是當代碼對錶中的數據進行更改時 - 實現一個不“執行”任何操作的測試模式。只是告訴我如果我願意的話會怎麼做。

當我爲just_checking參數傳遞TRUE(默認值)時,您可以在下面的代碼中看到它。

更好(?)Drop_Whatever

“?” 該標題只是提醒我們,這個程序本質上是危險的。

這是drop_whatever的版本,遵循我的建議。需要注意的是真實的,生產代碼,你應該  永遠不會  “報告”或通過調用DBMS_OUTPUT.PUT_LINE“日誌”的錯誤。誰會看到這個?相反,調用標準錯誤記錄過程,如果沒有,則獲取並使用  Logger

程序drop_whatever(
   nm               IN    VARCHAR2 DEFAULT '%'
,典型              IN    VARCHAR2 DEFAULT '%'
,just_checking    IN    BOOLEAN DEFAULT TRUE
AUTHID CURRENT_USER
IS
   PRAGMA AUTONOMOUS_TRANSACTION;
   dropstr VARCHAR2(32767);

   CURSOR object_cur
   IS
      SELECT object_name,object_type
        FROM user_objects
       WHERE object_name LIKE UPPER(nm)
         AND object_type LIKE UPPER(典型值)
         AND object_name <> 'DROP_WHATEVER' ;
開始
   FOR rec IN object_cur
   循環
      dropstr:=
            'DROP'
         || rec .object_type
         || ''
         || rec .object_name
         || 案件
               當rec .object_type  IN('TABLE','OBJECT')時
                  然後''CASCADE CONSTRAINTS'
               ELSE NULL
            結束;

      開始
         如果just_checking
         然後
            DBMS_OUTPUT .put_line(dropstr || ' - 只是檢查!');
         其他
            EXECUTE IMMEDIATE dropstr;
            DBMS_OUTPUT .put_line(dropstr || ' -  SUCCESSFUL!');
         萬一;

      例外
         其他時候
         然後
            DBMS_OUTPUT .put_line(dropstr || ' -  FAILURE!');
            DBMS_OUTPUT .put_line(DBMS_UTILITY .format_error_stack);
      結束;
   結束循環;
結束;


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