触发器中的 :OLD 和 :NEW 变量属于啥数据类型?

Posted

技术标签:

【中文标题】触发器中的 :OLD 和 :NEW 变量属于啥数据类型?【英文标题】:Of what data type are the :OLD and :NEW variables in a trigger?触发器中的 :OLD 和 :NEW 变量属于什么数据类型? 【发布时间】:2011-08-22 01:28:23 【问题描述】:

假设您在MY_CUSTOMER_TABLE 上有一个触发器,并且它有一个声明为MY_CUSTOMER_TABLE%ROWTYPE 类型的变量。如何将 OLD 值分配给该变量?

CREATE TRIGGER CUSTOMER_BEFORE
  BEFORE UPDATE ON MY_CUSTOMER_TABLE
  FOR EACH ROW
DECLARE
  old_version MY_CUSTOMER_TABLE%ROWTYPE;
BEGIN
  old_version := :OLD; /* Causes a PLS-00049 bad bind variable 'OLD' */
  old_version := OLD;  /* Causes a PLS-00201 identifier 'OLD' must be declared */
END;

编辑:

澄清一下,这是因为我使用触发器将行从MY_CUSTOMER_TABLE 归档到MY_CUSTOMER_TABLE_HISTORY。根据正在执行的操作(INSERTUPDATEDELETE),我需要来自OLDNEW 的所有字段:

CREATE TRIGGER CUSTOMER_BEFORE
  BEFORE UPDATE ON MY_CUSTOMER_TABLE
  FOR EACH ROW
DECLARE
  historical_record MY_CUSTOMER_TABLE_HISTORY%ROWTYPE;

  PROCEDURE
    copy
    (
      source_record      MY_CUSTOMER_TABLE%ROWTYPE,
      destination_record IN OUT MY_CUSTOMER_TABLE_HISTORY%ROWTYPE
    )
  BEGIN
    destination_record.customer_id   := source_record.customer_id;
    destination_record.first_name    := source_record.first_name;
    destination_record.last_name     := source_record.last_name;
    destination_record.date_of_birth := source_record.date_of_birth;
  END;

BEGIN
  /* I didn't want to replicate the same assignment statements for 
     each of the two cases: */
  CASE
    WHEN INSERT OR UPDATING THEN
      copy( source_record => :NEW, destination_record => historical_record );

    WHEN DELETING THEN
      copy( source_record => :OLD, destination_record => historical_record );

  END CASE;

  /* Some other assignments to historical_record fields... */

  INSERT INTO MY_CUSTOMER_TABLE_HISTORY VALUES historical_record;
END;

在这种情况下,PL/SQL 不允许我将 :OLD:NEW 传递给需要 MY_CUSTOMER_TABLE%ROWTYPE 参数的过程。

【问题讨论】:

这不起作用,您需要明确指定所有列...我也希望这样做。 Can I copy :OLD and :NEW pseudo-records in/to an Oracle stored procedure? 的可能重复项 【参考方案1】:

你不能。 引用所有列(如 SELECT *)通常是不好的做法,您应该指定所需的列。

【讨论】:

感谢您的回答。 :) 我同意您关于将所有列称为不好的做法的评论。但是,就我而言,我明确需要 all 列(希望在我的编辑中演示)。您是否有任何文件的链接确认无法完成?【参考方案2】:

在文档中,您会发现 :old 和 :new 是列值,而不是行类型。所以你必须手动构建你的行类型。

trigger ....
  l_row  mytable%rowtype;
begin
  l_row.column1 := :old.column1;
  l_row.column2 := :old.column2;
  ...

  archive_function(l_row);
end;

【讨论】:

【参考方案3】:

据我所知,:NEW 和 :OLD 的真正定义有点模糊。我已经看到它被称为对“伪记录”的引用。但是,看起来,Oracle 设置了对每个单独列的引用,而不是每个列在行类型中可引用的实际行类型,然后您可以使用 :NEW 和 :OLD 来引用它。但是,正如您所发现的, :NEW 和 :OLD 本身似乎不可引用。

例如,here。 (是的,我知道,这是一个 Java 参考,但请参阅关于 :OLD 本身不是有效参考的评论。

我还发现了这个注释 SYS.DBMS_DEBUG 包,它暗示 :NEW/:OLD 也不是有效的绑定。

-- get_value 和 set_value 现在支持绑定名称。绑定名称必须是 -- 加上引号并大写。请注意,触发器绑定具有 -- 限定名称,即 ":NEW" 不是有效绑定,而 ":NEW.CLMN" -- 有效。

this建议使用AFTER 触发器对您有用吗?从您的示例来看,似乎没有对值进行任何验证(意识到为简单起见,您可能没有将其放入示例中)。

我试图设想一种方法来动态(在您的触发器内)构造一个公共类型,该类型将使用 all_tab_columns 视图与您的表行类型匹配,然后将所有值填充到其中,但不能完全包装我的仔细研究这可能会如何摆脱的细节……如果它甚至可以工作的话。而且它最终可能比记录历史记录需要更多的工作!

【讨论】:

以上是关于触发器中的 :OLD 和 :NEW 变量属于啥数据类型?的主要内容,如果未能解决你的问题,请参考以下文章

[转]oracle触发器与:new,:old的使用

触发器 new和old before和after

触发器简介

关于mysql中触发器old和new

oracle new 和old 关键字

Oracle触发器