一张表Cd_Completion_t的非主键字段Completion_Description 要实现唯一性约束,因实际需要,字段Completion_Description 可以为空,可以为多个t,但是只允许有一个T,因此无法建唯一性索引,请问用触发器如何实现,我的触发器如下,但同时修改一下两条记录时有问题:
XJ7XVXqgzN t
XJ7XVXqgzN TCreate Or Replace Trigger Tg_Completion_Test
  After Insert Or Update On Cd_Completion_t
Declare
  Too_Many Exception;
  Pragma Exception_Init(Too_Many, -20001);
  v_Exist_Count Number := 0;
Begin
  Begin
    Select max(Count(*))
      Into v_Exist_Count
      From Cd_Completion_t t
     Where t.Completion_Description = 'T'
     Group By t.Well_Id;
  Exception
    When Others Then
      v_Exist_Count := 0;
  End;
  If v_Exist_Count > 1 Then
    Raise_Application_Error(-20001, '该井只能有一个主要生产层(T)!');
  End If;
End;

解决方案 »

  1.   

    CREATE OR REPLACE TRIGGER Tg_Completion_Test
      AFTER INSERT OR UPDATE ON Cd_Completion_t
      FOR EACH ROW --行触发,没有本行则为语句级触发
    DECLARE
      Too_Many EXCEPTION;
      PRAGMA EXCEPTION_INIT(Too_Many, -20001);
      v_Exist_Count NUMBER := 0;
    BEGIN
      BEGIN
        SELECT MAX(COUNT(*))
          INTO v_Exist_Count
          FROM Cd_Completion_t t
         WHERE t.Completion_Description = 'T'
         GROUP BY t.Well_Id;
      EXCEPTION
        WHEN OTHERS THEN
          v_Exist_Count := 0;
      END;
      IF v_Exist_Count > 1 THEN
        Raise_Application_Error(-20001, '该井只能有一个主要生产层(T)!');
      END IF;
    END;
      

  2.   

    Begin
      Select max(Count(*))
      Into v_Exist_Count
      From Cd_Completion_t t
      Where t.Completion_Description = 'T'
      Group By t.Well_Id;
      Exception
      When Others Then
      v_Exist_Count := 0;
      End;
      If v_Exist_Count > 1 Then
      Raise_Application_Error(-20001, '该井只能有一个主要生产层(T)!');
      End If;改为:
    Begin
      Select Count(*)
      Into v_Exist_Count
      From Cd_Completion_t t
      Where t.Completion_Description = 'T'
      Exception
      When Others Then
      v_Exist_Count := 0;
      End;
      If v_Exist_Count > 1 Then
      Raise_Application_Error(-20001, '该井只能有一个主要生产层(T)!');
      End If;
      

  3.   

    CREATE OR REPLACE TRIGGER Tg_Completion_Test
      AFTER INSERT OR UPDATE ON Cd_Completion_t
    FOR EACH ROW --行触发,没有本行则为语句级触发
    DECLARE
      Too_Many EXCEPTION;
      PRAGMA EXCEPTION_INIT(Too_Many, -20001);
      v_Exist_Count NUMBER := 0;
    BEGINBegin
      Select max(Count(*))
      Into v_Exist_Count
      From Cd_Completion_t t
      Where t.Completion_Description = 'T'
      Group By t.Well_Id;
      Exception
      When Others Then
      v_Exist_Count := 0;
      End;
      If v_Exist_Count > 1 Then
      Raise_Application_Error(-20001, '该井只能有一个主要生产层(T)!');
      End If;改为:
    CREATE OR REPLACE TRIGGER Tg_Completion_Test
      AFTER INSERT OR UPDATE ON Cd_Completion_t
    FOR EACH ROW --行触发,没有本行则为语句级触发
    DECLARE
      Too_Many EXCEPTION;
      PRAGMA EXCEPTION_INIT(Too_Many, -20001);
      v_Exist_Count NUMBER := 0;
    BEGINBegin
      Select Count(*)
      Into v_Exist_Count
      From Cd_Completion_t t
      Where t.Completion_Description = 'T'
             and t.Well_Id=:new.Well_Id
      Exception
      When Others Then
      v_Exist_Count := 0;
      End;
      If v_Exist_Count > 1 Then
      Raise_Application_Error(-20001, '该井只能有一个主要生产层(T)!');
      End If;
      

  4.   

    用行级触发,如果第二条已经为T,我修改第一条为T,会报错
    或者以下情况:
    第一条为t,第二条为T,我现在同时修改第一条为T,第二条为t
    以上各位的写法貌似都无法实现。
    同志们,继续……
      

  5.   

    --建议这种逻辑最好放到业务层或者通过程存储来实现。
    CREATE OR REPLACE TRIGGER Tg_Completion_Test
      AFTER INSERT OR UPDATE ON Cd_Completion_t
      FOR EACH ROW
      WHEN (new.Completion_Description='T')
    DECLARE
      Too_Many EXCEPTION;
      PRAGMA EXCEPTION_INIT(Too_Many, -20001);
      PRAGMA AUTONOMOUS_TRANSACTION;
      v_Exist_Count NUMBER := 0;
    BEGIN
      SELECT COUNT(*)
        INTO v_Exist_Count
        FROM Cd_Completion_t t
       WHERE t.Completion_Description = 'T'
         AND t.well_id = :new.well_id;
      IF v_Exist_Count > 0 THEN
        Raise_Application_Error(-20001, '该井只能有一个主要生产层(T)!');
      END IF;
    END;
    /
      

  6.   

    tangren兄:
    第一条为t,第二条为T,我现在同时修改第一条为T,第二条为t
    我现在最纠结的就是这一条有问题。
      

  7.   

    tangren兄:
    第一条为t,第二条为T,我现在同时修改第一条为T,第二条为t
    我现在最纠结的就是这一条有问题。
      

  8.   

    怎么没人遇到这种问题?
    Create unique index idx_cd_completon_t
    on cd_completion_t(Case when completion_description = 'T' then WELL_ID End);
    利用oracle自带的创建索引,也无法解决上面我说的那个同时改两条数据的问题,崩溃啊。
      

  9.   

    第一条为t,第二条为T,我现在同时修改第一条为T,第二条为t
    这种情况是符合你的规则,还是违反你的规则?
      

  10.   

    或者你认为在同一个事务里使用两条update语句(下面是伪码):update cd_completion_t set completion_description = 'T' where 第一条
    update cd_completion_t set completion_description = 't' where 第二条这种情况认为应该是符合你的规则?
      

  11.   

    直接使用update语句更新t的数据,更新T的数据就使用触发器来做:1.在insert之前,你应该确定表里面是否已经存在了一条T的数据,如果存在那么就取消insert,
    所以你的触发器应该是一个before触发器;2.在update之后,检查表里面的T的数据数量,如果大于1条,那么回滚刚刚的更新,
    所以应该建立一个after触发器;3.不过还是觉得麻烦,还是使用存储过程比较好些吧!