【问题标题】:Get Oracle PL/SQl variable property by name (reflection in PL/SQL)按名称获取 Oracle PL/SQl 变量属性(PL/SQL 中的反射)
【发布时间】:2011-04-12 02:21:42
【问题描述】:

我需要对更新一行进行一些审核。

所以我有一个函数接收 some_table%ROWTYPE 类型的参数,其中包含要为该行保存的新值。

我还需要在历史记录表中保存一些关于更改了哪些列值的信息。我正在考虑从 all_tab_columns 中获取 some_table 的列名,然后遍历这些以比较旧值和新值并查看它是否已更改。问题是一旦我有了列名,我就不知道如何访问我的 ROWTYPE 变量中的值。类似 var.getProperty(columnName)。

我想这样做是为了避免有一堆 IF,每个字段一个,而且它也可以直接在表中添加一个新列。

我也不能使用触发器,因为上级说“没有触发器!”。 (如果这确实是唯一的方法,我可以尝试再次与他们讨论这个问题)。

【问题讨论】:

  • 当然,有很多方法可以处理这个问题,但在我看来,触发器是维护历史表的最佳方法。
  • @BobJarvis - 触发器是调用某些代码的简单机制。我们在触发器中没有什么是我们不能在 Andrei 在这里描述的函数中做的。
  • @APC:我恭敬地提出,不同之处在于触发器在给定条件下提供自动执行,例如ON INSERT、ON DELETE 等。虽然可以在一个函数中实现相同的功能,但现在必须编写所有维护相关基表的代码来调用“历史表更新”函数,我的经验是这些类型的事情很容易被遗忘、忽视或绕过。如果@Andrei 的管理层下令“禁止触发!”历史表更新最好作为一个过程来实现,但 IMO 触发器会更好。 YMMV。

标签: oracle reflection plsql audit


【解决方案1】:

可能值得找出为什么存在“无触发器”规则。

有很多反对触发器的好论据 - 尤其是关于将业务规则放入触发器中 - 但日志记录通常被认为是使用它们的一个很好的案例。

Oracle 的内置表版本控制(每次更新记录一行)也值得一看——这使历史的形状与当前的表形状保持一致。 它不会为您提供“更改了什么”历史记录,但最好在查看历史记录时立即执行“更改了什么”,而不是在每次更新时增加成本。

我发现做你想做的事情的唯一方法 - 动态访问 %ROWTYPE 的属性,是将变量放在包头上(因此它是公开可见的),然后执行动态 PL/SQL。您可以封装行变量,只要您的动态 pl/sql 块在每次检查之前都包含一个本地副本。即把它想象成你的立即执行模板。

DECLARE
     lNew myTab%ROWTYPE;
     lOld myTab%ROWTYPE;
     lReturn PLS_INTEGER := 0;
BEGIN
    lNew := pStatefulPackage.NewRow;
    lOld := pStatefulPackage.OldRow;
    IF NVL(lNew.<variable>,'~') != NVL(lOld..<variable>,'~') THEN
        :lReturn := 1;
    END IF;
END;

解决您无法在动态 SQL 中绑定记录变量或布尔值这一事实非常麻烦。

它还增加了每列的大量开销。

最后,我发现 ALL_TAB_COLUMNS 太慢了,无法用于此类事情 - 您需要将元数据缓存在本地 pl/sql 内存中。

【讨论】:

  • 我认为“Oracle 的内置表版本控制”是指 11g 中引入的 Total Recall(又名闪回存档)。我同意这是一种很酷且最优雅的解决方案,但它仅作为企业版许可证的额外收费提供。
  • 谢谢,我最终做了类似的事情。
【解决方案2】:

通过从 USER_TAB_COLUMNS 读取列名来生成函数的主体可能是最简单的,例如(简化,不检查空值):

select 'if :old.'||column_name||' <> :new.'||column_name||' then log('''||column_name||''',:old.'||column_name||',:new.'||column_name||'); end if;'
  from user_tab_columns
  where table_name='MYTABLE';

【讨论】:

  • 我尝试在这样的事情上执行 EXECUTE,但我不能将我的 pl/sql 变量放在 USING 子句中来替换 :old 和 :new。它给出了一个错误,说它们不是 SQL 类型。
  • 啊,对不起, :old 和 :new 是典型的触发器。我的错。将它们替换为您的函数的参数名称(当然没有冒号)
猜你喜欢
  • 1970-01-01
  • 2015-07-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-04
  • 2010-09-20
相关资源
最近更新 更多