在触发器内的同一表上发出delete和insert语句时,如何避免重复错误?

时间:2018-07-19 03:00:47

标签: oracle triggers

我有一个触发器,可以通过删除和插入来同时修改表。但是我得到ORA-00001: unique constraint violated。我认为delete命令实际上没有在插入命令被触发之前提交,因为我不能在触发器中使用commit,所以我不知道如何摆脱该错误。

CREATE OR REPLACE TRIGGER "RESOURCEGROUP_AIU2" 
AFTER INSERT OR UPDATE
ON resourcegroup
BEGIN
FOR ROW IN (SELECT *
             FROM resourcegroup_moves)
LOOP
  IF ROW.oldpath IS NOT NULL
  THEN
     DELETE FROM mayaccessflat 
      WHERE resourceGroup <> inheritedFrom
        AND resourcegroup = ROW.id
        AND inheritedFrom IN (SELECT id
                                FROM resourcegroup
                               WHERE ROW.oldpath LIKE path||'/%');
  END IF;
  INSERT INTO mayaccessflat
             (resourcegroup, person, PROFILE, inheritedfrom)
    SELECT DISTINCT ROW.ID, ma.person, ma.PROFILE, rg.ID
      FROM mayaccess ma, resourcegroup rg
     WHERE ma.resourcegroup = rg.ID
       AND ma.inherit=1
       AND ROW.newpath LIKE rg.PATH || '/%';
END LOOP;
END;

我正在使用Oracle 6 DB。

2 个答案:

答案 0 :(得分:1)

问题与提交无关。 INSERT与DELETE在同一事务中发生。因此,问题必须是DELETE不会删除随后的语句正在插入的所有键。

一个原因可能是resourcegroup_moves中的记录内部不一致。或者它们可能与resourcegroup中的相关记录不一致。如果是这样,则INSERT会正确触发ORA-00001,并且解决方案将改进对resourcegroup_moves的验证。

或者,resourcegroup_moves中记录的可能是有效的,但是由于您是逐行而不是按组处理这些记录,因此会出现不一致的情况。您对DISTINCT的使用无法解决此问题,因为您没有处理整套设备。如果是这种情况,则可以使用基于集合的方法来解决它:

CREATE OR REPLACE TRIGGER "RESOURCEGROUP_AIU2" 
AFTER INSERT OR UPDATE
ON resourcegroup
BEGIN
  DELETE FROM mayaccessflat 
  WHERE resourceGroup <> inheritedFrom
  AND (resourcegroup, inheritedFrom) IN 
            (SELECT rgm.id, rg.id
             FROM resourcegroup_moves rgm, 
                   resourcegroup rg
              WHERE rgm.oldpath LIKE rg.path||'/%');

  INSERT INTO mayaccessflat
             (resourcegroup, person, PROFILE, inheritedfrom)
    SELECT DISTINCT rgm.ID, ma.person, ma.PROFILE, rg.ID
      FROM mayaccess ma
           , resourcegroup rg
           , resourcegroup_moves rgm
     WHERE ma.resourcegroup = rg.ID
       AND ma.inherit=1
       AND rgm.newpath LIKE rg.PATH || '/%';
END;

将这种业务逻辑放入触发器中并不是一个好习惯。作为存储过程,最好将验证规则应用于resourcegroupresourcegroup_moves

答案 1 :(得分:0)

您应该将唯一性约束设置为DEFERRABLE,然后在事务内部使用SET CONSTRAINTS name DEFERRED来推迟唯一性检查,直到事务提交为止。

请参见Orcale文档中的Specifying Constraint State