2012-11-29 162 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数据库管理员我在这一点上有点头痛,但仍会继续关注它。非常感谢您的帮助。 –