按名称获取 Oracle PL/SQl 变量属性(PL/SQL 中的反射)

发布于 2024-09-19 08:44:36 字数 361 浏览 7 评论 0原文

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

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

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

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

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

I need to do some auditing on updating a row.

So I have a function that receives a parameter of type some_table%ROWTYPE, containing the new values to be saved for that row.

I also need to save some info in a history table regarding what column values where changed. I was thinking of getting the column names for some_table from all_tab_columns, and then iterating over those to compare old and new values and see if it was changed. The problem is once I have the column name, I don't know how to access the value in my ROWTYPE variable. Something like var.getProperty(columnName).

I wanted to do it this way to avoid having a bunch of IFs, one for each field, and it would also work directly on adding a new column to the table.

Also I can't use triggers because the higher ups said "No triggers!". (If this is indeed the only way, I could try talking to them again about this).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

睫毛溺水了 2024-09-26 08:44:36

也许值得找出为什么有“无触发”规则。

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

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 内存中。

It might be worth finding out why there is a 'no triggers' rule.

There's a lot of good arguments against triggers - especially about putting business rules in triggers - but logging is generally accepted as a good case for their usage.

It's also worth looking at Oracle's built in table versioning (which records a row per update) - this keeps the shape of the history in line with the current table shape.
It doesn't give you the 'what changed' history, but it is probably better to do 'what changed' at the point when you are looking at the history, rather than adding the cost on every update.

The only way I've found of doing something like what you want - dynamically accessing a property of a %ROWTYPE, is to put a variable on a package header (so it is publicly visible) then doing dynamic PL/SQL. You could encapsulate the row variable, so long as your dynamic pl/sql block contained a local copy before each check. i.e. imagine this as your template for the execute immediate.

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;

Which is a lot of hassle to work around the fact you can't bind record variables or booleans in dynamic SQL.

It's also adding a lot of overhead on a per column basis.

Finally, I've found that ALL_TAB_COLUMNS is too slow to use for this sort of thing - you'd need to cache the metadata in local pl/sql memory.

浸婚纱 2024-09-26 08:44:36

通过从 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';

It might be easiest to generate the body of the function, by reading the column names from USER_TAB_COLUMNS, e.g. (simplified, doesn't check for nulls):

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';
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文