创建触发器的问题(对于主键序列)

时间:2011-05-19 06:51:15

标签: sql oracle oracle11g ora-00001

我创建了一个以1开头但没有最大值的序列 我已经自动创建了一个主键插入触发器,如下所示 我还为表设置了一个约束,其中主键必须是唯一的而不是null

create trigger MY_TEMP_TRIGGER
before insert on MY_TEMP
for each row

BEGIN

  SELECT MY_TEMP_SEQ.nextval 
    INTO :new.Id 
    FROM DUAL;

END;
/

INSERT INTO my_temp  
  (Id,Type, CreateDT, TypeId, TempType, DevType, Msg, File,User, Src, SrcDev)   
VALUES
 (MY_TEMP_SEQ.nextval,3434,2843,2453,2392,435,2390,'pension.txt','rereee',454545,3434)

结果:

  

第1行的错误:
  ORA-00001:违反了唯一约束(USER.PK_MY_TEMP)

表MY_TEMP已包含从Id字段<1/338开始的值

那么,我应该如何在触发器和插入语句中处理它。

3 个答案:

答案 0 :(得分:1)

如果你真的希望选项在insert上指定你自己的ID值,并且在其他时候使用序列依赖于触发器,那么你的触发器需要检查它是否传递了值 - 否则触发器生成的ID将优先(如果您在插入中指定MY_TEMP_SEQ.nextval,将跳过该值。)

BEGIN
    IF :NEW.id IS NULL THEN
        SELECT MY_TEMP_SEQ.nextval 
        INTO :NEW.id 
        FROM DUAL;
    END IF;
END;

处理已存在的值更复杂。如果您不打算传递自己的(非序列)ID值,那么您可以将序列向前滚动到最高的现有值,例如:

ALTER SEQUENCE MY_TEMP_SEQ INCREMENT BY 338; -- or however many you need to skip
SELECT MY_TEMP_SEQ.nextval FROM DUAL;
ALTER SEQUENCE MY_TEMP_SEQ INCREMENT BY 1;

如果您尝试插入记录手动指定ID值而不使用序列(大于序列(例如使用500)),那么您将遇到问题。当序列(最终)达到该值时,您仍将获得ORA-00001。

我认为你不能在触发器内处理这个或者你的直接问题;我相信如果你试图检查同一个表中的现有值,你会得到一个变异表错误,并且这个的变通方法只会增加复杂性(需要三个触发器)并且可能不稳定。据我所知,处理该场景的唯一简单方法是将插入包装在一个过程中,并阻止直接插入,这也许不是一种选择。如果您在不使用序列的情况下插入值,则只需要担心这一点。

答案 1 :(得分:0)

你不能同时使用两者 - 只有一种 在您的示例中,id值已作为序列的下一个值插入...这与触发器尝试使用的值相同。我的订单可能会倒退,但结果是一样的。

如果您要在INSERT语句中引用序列,则不需要触发器:

INSERT INTO my_temp  
  (Id,Type, CreateDT, TypeId, TempType, DevType, Msg, File,User, Src, SrcDev)   
VALUES
  (MY_TEMP_SEQ.nextval,3434,2843,2453,2392,435,2390,'pension.txt','rereee',454545,3434);

如果使用触发器

拥有触发器意味着您无法使用INSERT中的id列:

INSERT INTO my_temp  
  (Type, CreateDT, TypeId, TempType, DevType, Msg, File,User, Src, SrcDev)   
VALUES
  (3434, 2843, 2453, 2392, 435, 2390, 'pension.txt', 'rereee', 454545, 3434);

最像触发器方法,因为它们习惯于MySQL AUTOINCREMENT或SQL Server的IDENTITY(Denali最终将支持ANSI序列)。

答案 2 :(得分:0)

您可以在安装触发器之前递增序列:

declare 
  v_max_id my_temp.id%type;
  v_curr_seq  NUMBER;
begin
  select max(id) into v_max_id from my_temp;

  loop
    select MY_TEMP_SEQ.nextval into v_curr_seq from dual;
    exit when v_curr_seq >= v_max_id;
  end loop;
end;
/