我可以将 :OLD 和 :NEW 伪记录复制到 Oracle 存储过程中吗?

发布于 2024-08-22 19:46:53 字数 1438 浏览 6 评论 0原文

我有一个 AFTER INSERT OR UPDATE OR DELETE 触发器,我正在编写该触发器,通过复制 INSERTUPDATE 来存储某个表中发生的每个记录修订 :NEW 值放入镜像表中,DELETE 则放入 :OLD 值。

通过有条件地将 :NEW:OLD 记录传递到一个过程中,然后该过程将插入到我的历史表中,我可以大大整理我的代码。不幸的是,我似乎找不到传递整个 :OLD:NEW 记录的方法。

我是否遗漏了某些内容,或者在调用插入过程时是否无法避免枚举每个 :NEW:OLD 列?

我想做以下事情:

DECLARE
  PROCEDURE LOCAL_INSERT(historyRecord in ACCT.ACCOUNTS%ROWTYPE) IS
  BEGIN
    INSERT INTO ACCT.ACCOUNTS_HISTORY (ID, NAME, DESCRIPTION, DATE) VALUES (historyRecord.ID, historyRecord.NAME, historyRecord.DESCRIPTION, SYSDATE);
  END;
BEGIN
  IF INSERTING OR UPDATING THEN
    LOCAL_INSERT(:NEW);
  ELSE --DELETING
    LOCAL_INSERT(:OLD);
  END IF;
END;

但我坚持这样做:

DECLARE
  PROCEDURE LOCAL_INSERT(id in ACCT.ACCOUNTS.ID%TYPE,
                         name in ACCT.ACCOUNTS.NAME%TYPE,
                         description in ACCT.ACCOUNTS.DESCRIPTION%TYPE) IS
  BEGIN
    INSERT INTO ACCT.ACCOUNTS_HISTORY (ID, NAME, DESCRIPTION, DATE) VALUES (id, name, description, SYSDATE);
  END;
BEGIN
  IF INSERTING OR UPDATING THEN
    LOCAL_INSERT(:NEW.ID, :NEW.NAME, :NEW.DESCRIPTION);
  ELSE --DELETING
    LOCAL_INSERT(:OLD.ID, :OLD.NAME, :OLD.DESCRIPTION);
  END IF;
END;

好的,所以它看起来没有很大的区别,但这只是一个包含 3 列而不是数十列的示例。

I have an AFTER INSERT OR UPDATE OR DELETE trigger that I'm writing to store every record revision that occurs in a certain table, by copying the INSERT and UPDATE :NEW values into a mirror table, and for DELETE the :OLD values.

I could un-clutter my code considerably by conditionally passing either the :NEW or :OLD record into a procedure which would then do the insert into my history table. Unfortunately I cannot seem to find a way to pass the entire :OLD or :NEW record.

Am I missing something or is there no way to avoid enumerating every :NEW and :OLD column as I invoke my insert procedure?

I want to do the following:

DECLARE
  PROCEDURE LOCAL_INSERT(historyRecord in ACCT.ACCOUNTS%ROWTYPE) IS
  BEGIN
    INSERT INTO ACCT.ACCOUNTS_HISTORY (ID, NAME, DESCRIPTION, DATE) VALUES (historyRecord.ID, historyRecord.NAME, historyRecord.DESCRIPTION, SYSDATE);
  END;
BEGIN
  IF INSERTING OR UPDATING THEN
    LOCAL_INSERT(:NEW);
  ELSE --DELETING
    LOCAL_INSERT(:OLD);
  END IF;
END;

But I'm stuck doing this:

DECLARE
  PROCEDURE LOCAL_INSERT(id in ACCT.ACCOUNTS.ID%TYPE,
                         name in ACCT.ACCOUNTS.NAME%TYPE,
                         description in ACCT.ACCOUNTS.DESCRIPTION%TYPE) IS
  BEGIN
    INSERT INTO ACCT.ACCOUNTS_HISTORY (ID, NAME, DESCRIPTION, DATE) VALUES (id, name, description, SYSDATE);
  END;
BEGIN
  IF INSERTING OR UPDATING THEN
    LOCAL_INSERT(:NEW.ID, :NEW.NAME, :NEW.DESCRIPTION);
  ELSE --DELETING
    LOCAL_INSERT(:OLD.ID, :OLD.NAME, :OLD.DESCRIPTION);
  END IF;
END;

Okay, so it doesn't look like a big difference, but this is just an example with 3 columns rather than dozens.

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

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

发布评论

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

评论(4

赠佳期 2024-08-29 19:46:53

事实并非如此。你必须自己通过枚举来完成。

它不能/不自动工作的原因包括:

  • :old:new 是默认约定;您可以通过 CREATE TRIGGERREFERENCING 子句将 :old:new 引用命名为您想要的任何名称> 语句。

  • 您必须有一个类型的公共声明(通过CREATE TYPE或通过包声明)才能将其用作另一段代码的参数。

  • 触发代码是解释代码,而不是编译代码。

It isn't. You have to do it yourself through enumeration.

The reasons it can't/doesn't work automatically include:

  • the :old and :new are default conventions; you can name the :old and :new references to be whatever you want through the REFERENCING clause of the CREATE TRIGGER statement.

  • you'd have to have a public declaration of a type (through CREATE TYPE or through a package declaration) to be able to use it as an argument to another piece of code.

  • trigger code is interpreted code, not compiled code.

天涯离梦残月幽梦 2024-08-29 19:46:53

我认为这是不可能的。 文档没有提到类似的内容。

这肯定会降低性能,但您可以尝试定义触发器AFTER INSERT和另一个BEFORE UPDATE OR DELETE,并在触发器中执行类似以下操作:

SELECT *
INTO rowtype_variable
FROM accounts
WHERE accounts.id = :NEW.id; -- :OLD.id for UPDATE and DELETE

然后调用您的触发器使用该 rowtype_variable 的过程。

I don't think it's possible like that. Documentation doesn't mention anything like that.

This would certainly cost performance, but you could try to define your trigger AFTER INSERT and another one BEFORE UPDATE OR DELETE, and in the trigger do something like:

SELECT *
INTO rowtype_variable
FROM accounts
WHERE accounts.id = :NEW.id; -- :OLD.id for UPDATE and DELETE

and then call your procedure with that rowtype_variable.

扎心 2024-08-29 19:46:53

使用SQL生成SQL;

select ' row_field.'||COLUMN_NAME||' := :new.'||COLUMN_NAME||';'  from 
    ALL_TAB_COLUMNS cols 
where 
    cols.TABLE_NAME = 'yourTableName'
order by cols.column_name.

然后复制并粘贴输出。

Use SQL to generate the SQL;

select ' row_field.'||COLUMN_NAME||' := :new.'||COLUMN_NAME||';'  from 
    ALL_TAB_COLUMNS cols 
where 
    cols.TABLE_NAME = 'yourTableName'
order by cols.column_name.

Then copy and paste output.

月隐月明月朦胧 2024-08-29 19:46:53

如果您使用AFTER触发器,您可以使用rowid作为参数来调用过程

insert into t_hist
select * from t where rowid = r;

如果您使用BEFORE触发器,您将得到ORA-04091 变异表,但是您的解决方案可以是(http://www.dba-oracle.com/t_avoiding_mutating_table_error.htm):

  • 不要使用触发器 - 避免变异表错误的最佳方法是不使用触发器。虽然面向对象的 Oracle 提供了与表关联的“方法”,但大多数精明的 PL/SQL 开发人员都会避免使用触发器,除非绝对必要。
  • 使用“after”或“instead of”触发器 - 如果必须使用触发器,最好通过使用“after”触发器来避免变异表错误,从而避免与变异表相关的货币问题。例如,使用触发器“:after update on xxx”,原始更新已完成,表不会发生变化。
  • 重新设计触发器语法 - Hall 博士对改变表错误有一些很好的注释,并提供了其他方法来避免通过行级和语句级触发器的组合来改变表。
  • 使用自治事务 - 您可以通过将触发器标记为自治事务来避免变异表错误,使其独立于调用该过程的表。

If you use AFTER trigger you can use rowid as parameter to call procedure

insert into t_hist
select * from t where rowid = r;

If you use BEFORE trigger you will get ORA-04091 mutating table, BUT you solution can be (http://www.dba-oracle.com/t_avoiding_mutating_table_error.htm):

  • Don't use triggers - The best way to avoid the mutating table error is not to use triggers. While the object-oriented Oracle provides "methods" that are associated with tables, most savvy PL/SQL developers avoid triggers unless absolutely necessary.
  • Use an "after" or "instead of" trigger - If you must use a trigger, it's best to avoid the mutating table error by using an "after" trigger, to avoid the currency issues associated with a mutating table. For example, using a trigger ":after update on xxx", the original update has completed and the table will not be mutating.
  • Re-work the trigger syntax - Dr. Hall has some great notes on mutating table errors, and offers other ways to avoid mutating tables with a combination of row-level and statement-level triggers.
  • Use autonomous transactions - You can avoid the mutating table error by marking your trigger as an autonomous transaction, making it independent from the table that calls the procedure.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文