Sql Oracle触发器-更新行而不是删除

Sql Oracle触发器-更新行而不是删除,sql,oracle,triggers,Sql,Oracle,Triggers,如何编写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' AN

如何编写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;

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

首先,您编写的触发器将抛出一个变异表错误。技术上,你所要求的是不可能的,即删除不会删除,而是更新,除非你在中间引发异常,这可能是一个丑陋的做法。我认为用户使用某种应用程序前端,可以使用delete按钮
delete
数据,因此您可以在那里使用update语句,而不是
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;

如果确实需要触发器,更合理的方法是创建一个视图,在视图上创建一个
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;
创建视图vw\u m\u项目
作为
挑选*
从m_项目h;
创建或替换触发器t4m_项删除
而不是删除vw\u m\u项目
每行
作为
开始
更新m_项目
设置项_stat='D'
WHERE=:old。;
结束;
更好的是,您可以省去触发器,创建一个
delete\u item
过程,应用程序将调用该过程,而不是发出
delete
,该过程只需更新行以设置
item\u stat
列,而不是删除行

如果您真的,真的,真的想要一个解决方案,其中包括表本身的触发器,那么您可以

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

  • 这将比使用
    而不是触发器
    需要更多的工作,因为您必须删除行,然后重新插入行,这将涉及更多的移动片段,因此它将不那么优雅。但它会起作用。

    谢谢你的建议!我没有访问该应用程序的权限。基本上,它是一个仓库管理系统,如果该产品没有任何移动,它将删除一个项目代码。如果有,它只是停用该项目。我希望它总是停用该项目。该界面使用的是Oracle Forms,因此它非常难看,因此我不担心用户是否看到Oracle错误,因为当前的应用程序与它们分散在一起。如果您可以更改Oracle Forms代码以更改“删除”按钮功能,您就可以做到这一点。或者,在raise_应用程序_错误中,代替“无法删除项目”消息,显示类似“项目已删除/停用”的内容。不幸的是,我无法访问该应用程序,因此无法更改this@Lock-您是说这是一个第三方打包应用程序?如果是这样的话,为什么首先要通过添加触发器来修改它呢?因为我有sysdba访问database@Lock-如果这是第三方打包的应用程序,供应商通常不允许您首先向系统添加触发器。从技术上讲,您可以访问系统以添加触发器。但这样做会违反你的支持合同。如果您不介意违反支持合同,我会继续创建一个视图。您可以重命名该表,使用该表的名称创建视图,并在视图上创建
    而不是
    触发器。这通常对应用程序是透明的。