雖然努力改善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位)上試用過,它就能正常工作。爲什麼地球上這樣一個基本要求在兩個版本中表現出如此不同的行爲?它可以在兩個版本中使用相同的代碼工作嗎?
關於「其他」異常及其相應的註釋,除了表不存在以外,「drop table」命令可能會失敗。明確處理-942異常並讓其他人正常失敗會更好。 –
的確如此,但實際的例外情況對我手邊的場景並不感興趣 - 據我記憶 - 現在是3年半前! –