【发布时间】:2023-04-10 06:01:01
【问题描述】:
在尝试在我们的一个系统中提高 SP 的可维护性时,我决定使用循环比硬编码一组值(在本例中为表名)更好,并尝试相应地重构代码这样在系统中添加或删除表不需要编辑数组。暂且不说循环的原因和原因(我非常清楚反对它们的论点),谁能解释发生了什么?
想象两个用户 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;
运行此之后,WAS 删除的唯一表就是我不想删除的那个!更奇怪的是循环只执行了一次,就好像选择查询只返回一行一样。如果我在 SQL Developer 3.2 的调试中运行该过程,我可以看到它正在发生。我们在同事的 PC 上的 SQL Developer(可能是 3.1)上做了同样的事情,循环只执行了一次,但这次它正确地决定不删除表 MIDBLOG 并再次留下其他所有内容。
如果我在 SQL Developer 中将上述示例中的任何一个作为匿名块运行,它就会完全按照我的预期运行。我尝试了更详细的显式游标声明,结果与以前相同。我从来没有得到任何例外。
所有这些都在 Oracle 10g 企业版版本 10.2.0.4.0(64 位)上。只要在 Oracle 11g Enterprise Edition Release 11.2.0.1.0 (64bit) 上尝试过,它就可以正常工作。为什么这样一个基本要求会在两个版本中表现出如此截然不同的行为?它可以在两个版本中使用相同的代码按我想要的方式工作吗?
【问题讨论】:
-
关于
others异常及其附带的注释 - 除了表不存在之外,drop table命令命令可能失败的原因有很多。最好明确处理 -942 异常,让其他异常正常失败。 -
确实如此,但据我所知,实际的例外情况对手头的场景并不感兴趣 - 现在是 3 1/2 年前!
标签: stored-procedures for-loop plsql oracle10g oracle11g