【问题标题】:Problem with the trigger created(Fro primary key sequence)创建触发器的问题(来自主键序列)
【发布时间】:2011-05-19 06:51:14
【问题描述】:

我创建了一个从 1 开始没有最大值的序列 我已经创建了一个自动插入主键的触发器,如下所示 我还为主键必须唯一且不为空的表设置了约束

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 的值

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

【问题讨论】:

  • 不要忘记检查您的序列是否返回了表中尚不存在的值(并且高于现有值)。

标签: sql oracle oracle11g ora-00001


【解决方案1】:

如果您确实希望该选项在插入时指定您自己的 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。

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

【讨论】:

  • 我认为您可以捕获异常并将 max(id) 选择到变量中,然后使用立即执行来更改序列并最终重做插入。如果您预计异常可能会在同一事务中再次引发第二次,那么您可以在一个过程中执行所有操作并在异常处理程序中递归调用该过程
  • @christian - 如果您尝试在触发器中选择 max(id),您不会收到变异表错误吗?或者您是在谈论始终使用包装程序进行插入?
【解决方案2】:

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

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;
/

【讨论】:

    【解决方案3】:

    您不能同时使用两者 - 只能使用其中一个。
    在您的示例中,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 序列)。

    【讨论】:

    • 你所说的绝对正确。但我的问题是我的表中的数据集已经从 1 开始。现在我创建了一个也从 1 开始的序列。所以我从重复中得到一个 voilation。从技术上讲,我可以删除序列并使用从表的数据集的最大值开始的值创建一个,这将起作用。但这不是好方法。有什么方法可以修改触发器。
    • 任何人都可以查看以上内容
    猜你喜欢
    • 1970-01-01
    • 2011-02-15
    • 1970-01-01
    • 1970-01-01
    • 2013-06-03
    • 1970-01-01
    • 2014-10-28
    • 1970-01-01
    相关资源
    最近更新 更多