3

我如何编写 Oracle 触发器,而不是当用户删除某个记录时,删除实际上并没有发生,而是对这些行执行更新并将记录的状态设置为“D”?

我试过了:

create or replace
trigger DELFOUR.T4M_ITEM_ONDELETE
before delete on M_ITEM_H
FOR EACH ROW
BEGIN
  UPDATE
    M_ITEM_H
  SET
    ITEM_STAT = 'D'
  WHERE
    CUST_CODE = 'TEST'
    AND ITEM_CODE = 'GDAY'
  ;

  raise_application_error(-20000,'Cannot delete item');
END;

但是我遇到了变异表错误。这可能吗?

4

2 回答 2

5

如果你真的需要一个触发器,更合乎逻辑的方法是创建一个视图,INSEAD OF DELETE在视图上创建一个触发器,并强制应用程序对视图而不是基表发出删除。

CREATE VIEW vw_m_item_h
AS
SELECT *
  FROM m_item_h;

CREATE OR REPLACE TRIGGER t4m_item_ondelete
  INSTEAD OF DELETE ON vw_m_item_h
  FOR EACH ROW
AS
BEGIN
  UPDATE m_item_h
     SET item_stat = 'D'
   WHERE <<primary key>> = :old.<<primary key>>;
END;

更好的是,您将省去触发器,创建一个delete_item您的应用程序将调用的过程而不是发出一个DELETE过程,并且该过程将简单地更新行以设置item_stat列而不是删除行。

如果您真的,真的,真的想要一个涉及表本身触发器的解决方案,您可以

  1. m_item_h创建一个包含一个成员的包,该成员是映射到表中数据的记录集合
  2. 创建清空此集合的删除前语句级触发器
  3. 创建一个删除前的行级触发器,将:old.<<primary key>>和所有其他:old值插入到集合中
  4. 创建一个 after delete 语句级触发器,该触发器遍历集合,将行重新插入表中,并设置item_stat列。

这将比 a 涉及更多的工作,instead of trigger因为您必须删除然后重新插入该行,并且它会涉及更多的移动部分,因此它会不那么优雅。但它会起作用。

于 2012-09-05T02:20:09.227 回答
4

首先,您编写的触发器会引发变异表错误。从技术上讲,您要问的是不可能的,即删除不会删除而是更新,除非您在中间引发异常,这可能是一种丑陋的做法。我认为用户使用某种应用程序前端,它允许他们delete使用删除按钮来获取数据,因此您可以在那里使用更新语句而不是delete语句。

另一种选择是创建一个日志表,您可以在其中插入记录,然后将其从实际表中删除,然后将日志表与实际表连接以检索已删除的记录。就像是-

 CRETAE TABLE M_ITEM_H_DEL_LOG as SELECT * FROM M_ITEM_H WHERE 1=2;

接着

create or replace
trigger DELFOUR.T4M_ITEM_ONDELETE
before delete on M_ITEM_H
FOR EACH ROW
BEGIN
  INSERT INTO
    M_ITEM_H_DEL_LOG
  VALUES (:old.col1, :old.col2,.....) --col1, col2...are columns in M_ITEM_H
  ;
END;
于 2012-09-05T02:17:34.963 回答