2012-11-29 97 views
0

雖然努力改善SP的可維護性在我們的一個系統,我決定,使用循環會比其值(表名在這種情況下)的陣列更好的硬編碼並試圖相應地重新分配代碼,以便向系統添加或刪除表格不需要編輯數組。撇開目前的循環(我知道反對他們的觀點),任何人都可以解釋發生了什麼?甲骨文For循環存儲過程中不循環

想象一下兩個用戶,SOURCEUSER和DestUser都在同一個數據庫中,每個與他們在同一個表空間中的表。 SourceUser中的一堆存儲過程將來自SourceUser的數據填充到DestUser中以用於報告目的。作爲其中的一部分,要運行的第一個程序會丟棄DestUser中的所有表並重新創建它們。再次,不要在這裏辯論這樣做的相對優點。

SOURCEUSER具有降大任表和DestUser創建的任何表的權限。 DestUser中有一張我們想要保留的表格。所以,我的方法構建的SQL是這樣的:

Begin 
    For T In (SELECT TABLE_NAME FROM all_tables WHERE TABLE_NAME != 'MIDBLOG' AND OWNER = sTarget_DB) Loop 
    Begin 
     Execute Immediate('Drop Table ' || sTarget_DB || '.' || T.TABLE_NAME); 

    Exception 
     When Others Then 
     --Don't care if we get an exception here as most likely the table wasn't there to be dropped in the first place. 
     NULL; 
    End; 
    End Loop; 
End; 

在這種情況下sTarget_DB設置爲DestUser,這個代碼正在對SOURCEUSER運行。

當程序運行我發現沒有表已被刪除(我證實,有一對夫婦十幾桌,包括一個開始前命名MIDBLOG)。我在SQL Developer調試模式下運行它,並且執行甚至從未進入循環內部,因爲它似乎認爲它沒有要處理的行,但我確定select語句會返回幾十個表名。

接下來我將它修改爲這樣:

Begin 
    For T In (SELECT TABLE_NAME FROM all_tables WHERE OWNER = sTarget_DB) Loop 
    Begin 
     If T.TABLE_NAME != 'MIDBLOG' THEN 
     Execute Immediate('Drop Table ' || sTarget_DB || '.' || T.TABLE_NAME); 
     End If; 
    Exception 
     When Others Then 
     --Don't care if we get an exception here as most likely the table wasn't there to be dropped in the first place. 
     NULL; 
    End; 
    End Loop; 
End; 

運行此被刪除是非常一個我不想刪除的唯一表後!更奇怪的是,循環只執行一次,就像select查詢只返回一行一樣。如果我在SQL Developer 3.2的調試中運行該過程,我可以看到它發生了。我們在SQL Developer的同事電腦上做了同樣的事情(可能是3.1),循環只執行一次,但這次它正確地決定不放棄表MIDBLOG,並且再次將所有其他事情放在一邊。

如果我運行以上任一例子,如SQL Developer中匿名塊它不正是我希望做的事。我嘗試了更詳細的顯式遊標聲明,並得到與以前相同的結果。我從來沒有得到任何例外。

所有這一切都是在Oracle 10g企業版發佈10.2.0.4.0(64位)。只要在Oracle 11g企業版版本11.2.0.1.0(64位)上試用過,它就能正常工作。爲什麼地球上這樣一個基本要求在兩個版本中表現出如此不同的行爲?它可以在兩個版本中使用相同的代碼工作嗎?

+0

關於「其他」異常及其相應的註釋,除了表不存在以外,「drop table」命令可能會失敗。明確處理-942異常並讓其他人正常失敗會更好。 –

+0

的確如此,但實際的例外情況對我手邊的場景並不感興趣 - 據我記憶 - 現在是3年半前! –

回答

2

我的猜測是,問題與特權相關,而不是Oracle的版本。在DestUser上的特權是否授予SrcUser通過一個數據庫中的角色並通過另一個數據庫中的直接授予?

在運行匿名PL/SQL塊之前,如果先禁用角色會發生什麼?

set role none; 
<<run the anonymous PL/SQL block>> 

如果添加儀表的代碼,確實查詢對all_tables回報設定您所期望的表?我的猜測是,當代碼失敗時,它在一個定義者的權限存儲過程中,過程的所有者可以通過角色訪問DestUser表。由於通過角色授予的權限在定義者的權限存儲過程中不可見,因此這會導致循環中的SELECT語句返回0行(儘管以交互方式運行相同的查詢將返回您期望的行)。另一方面,如果直接授予DestUser表中的特權,則相同的定義者權限存儲過程將成功運行。它可以在一個匿名的PL/SQL塊中工作。

+0

我會檢查任何角色等,但在所有情況下,贈款是直接的,而不是角色。令我感到困惑的是,爲什麼一個人在10g服務器上得到0行而另一個人得到1行。兩者都使用相同的登錄名,因此具有相同的權限。我會嘗試禁用角色並回報。針對all_tables的查詢返回的結果與我期望的完全一致,這就是爲什麼我不明白計算機和服務器之間的差異。 –

+0

好吧,正如你所建議的,我已經嘗試在兩臺服務器上運行匿名塊之前禁用所有角色,並且確信結果也是你的建議:設置角色none =>匿名塊不執行任何操作並在其自己的產品上運行select沒有結果。我不得不得出結論,我們在兩臺服務器之間的設置不一致,所以如果我想堅持這一點,我需要調查我們在我們(和我們的客戶)服務器上的角色。恥辱,比一系列表名稱更乾淨的解決方案! –

+0

由於開發進度的壓力,這將有一邊繼續作爲一個學者,但令人擔憂的是我們的用戶(SOURCEUSER和DestUser)被授予我本來以爲會是DBA角色足夠了,但也許SP的是一些其它的授權下執行系統用戶。不是Oracle數據庫管理員我在這一點上有點頭痛,但仍會繼續關注它。非常感謝您的幫助。 –