oracle DB 中的自动增量替代方案

Posted

技术标签:

【中文标题】oracle DB 中的自动增量替代方案【英文标题】:Auto increment alternatives in oracle DB 【发布时间】:2013-12-07 17:57:47 【问题描述】:

我对 Oracle 比较陌生,过去几个小时一直在浏览网页,但仍然无法找到困扰我的问题的明确答案。

我来自软件工程背景,通过为每个表创建 AI 序列来重复代码的数量让我感到沮丧。无论如何我可以定义一个 AI 函数来获取序列和表的名称,这样我就可以在触发器中调用该函数,还是需要对每个序列进行硬编码?

我目前对我拥有的 12 个表中的每一个都有这样的序列

CREATE OR REPLACE TRIGGER trg_site_addr
BEFORE INSERT OR UPDATE ON site_addr FOR EACH ROW
BEGIN 
IF INSERTING THEN
    IF :NEW.site_addr_id IS NULL THEN
        SELECT seq_site_addr_id.nextval
        INTO :NEW.site_addr_id
        FROM sys.dual;
    END IF;
END IF;
END;

理想情况下,我希望能够做到以下几点

CREATE OR REPLACE TRIGGER trg_site_addr
BEFORE INSERT OR UPDATE ON site_addr FOR EACH ROW
BEGIN 
IF INSERTING THEN
    IF :NEW.site_addr_id IS NULL THEN
        -- Param 1 = sequence name, Parm 2 = table name
        auto_increment(seq_site_addr_id, site_addr);
    END IF;
END IF;
END;

我将如何去做,任何指针将不胜感激。

亚历克斯。

* 编辑 * 为了消除任何混淆,我想创建一个函数,但我不知道是否可以在函数中嵌入序列然后使用参数,即

CREATE FUNCTION auto_increment( seq_name, table_col_id)
       SELECT seq_name.nextval
       INTO :NEW.table_col_id
       FROM sys.dual;
END;

这样,它将在触发器中为每个表动态创建增量序列,而无需我对每个表的 AI 触发器进行硬编码。

如果这不可能,那么硬编码没什么大不了的,这只是我的一个查询。

【问题讨论】:

您是否尝试过实际的SEQUENCE?见***.com/questions/7681122/… 和docs.oracle.com/cd/B28359_01/server.111/b28286/… 所以你想创建类似 for 循环的东西? 升级到12c(如果可以的话)并使用identity columns 为什么不对所有表格使用相同的顺序? “Oracle”的方式是直接在INSERT语句中调用你的序列:insert into mytable (id, ... ) values (myseq.nextval, ...)。就我个人而言,我发现这比自动增量列好得多。而且触发器很臭,除非我没有更明智的选择,否则我不会使用它们...... 【参考方案1】:

另一种方法是使用一个表来存储其他表的 ID。 喜欢:

对于应用程序的任何表(如客户表),我们都需要执行以下操作:

insert into ID_Table (table_name, current_id) values ('CUSTOMER', 0);

获取新 id 的函数是:

CREATE OR REPLACE FUNCTION fetch_new_id(p_table_name Varchar2) return number
IS
   result_value number;
BEGIN    
       SELECT ID_Tbale.current_id + 1
       INTO result_value 
       FROM ID_Table where trim(upper(ID_Table.table_name)) = trim(upper(p_table_name))
      for update;

      UPDATE ID_Tbale set current_id =  current_id + 1
      where trim(upper(ID_Table.table_name)) = trim(upper(p_table_name));

      COMMIT;

      RETURN result_value ;
      -- exception handling, etc
END;

【讨论】:

如果你要走这条路,我认为使用 RETURNING: update id_table set current_id = current_id + 1 where table_name = p_table_name returning current_id into result_value 的单个更新更干净 @gpeche 我故意使用for update 来保持并发性并防止丢失更新。 @Moshen Yedari 我知道,我的意思是,如果你在一个 UPDATE ... RETURNING ... 中完成所有操作,它会更干净,它还保持并发性并通过单个 SQL 语句防止丢失更新。 这个答案让我身体不适。 @clever-idea-widgetry 让我们考虑一下 Oracle 如何在内部实现序列?或者考虑一个不支持自动增量概念的数据库。你的解决方案是什么?【参考方案2】:

我不推荐它,但这样的事情应该或多或少地做你想要的:

CREATE FUNCTION auto_increment(seq_name in varchar2) RETURN NUMBER IS
  next_seq number;
BEGIN
   -- WARNING: use with care, possible SQL injections?
   execute immediate 'select ' || seq_name || '.nextval from dual' into next_seq;
   return next_seq
END auto_increment;

-- In trigger code
...
if inserting then
  if :new.id is null then
    :new.id := auto_increment('myseq');
  end if;
end if;
...

最好的方法是在INSERT 语句中嵌入序列调用。如果INSERT 语句无法修改,我只会使用触发器(也许它们是某些第 3 方软件的一部分,假设世界上每个数据库都支持自动增量列)。

【讨论】:

以上是关于oracle DB 中的自动增量替代方案的主要内容,如果未能解决你的问题,请参考以下文章

Oracle 包中的自动增量

Oracle 的自动增量

用于Java开发人员的Oracle数据库中的自动增量

Java和mySQL DB中的自动增量ID字段?

使用 NetBeans IDE 在 Java DB 中创建自动增量键

org.hibernate.exception.ConstraintViolationException:无法更新 - DB 中的自动增量列为 NULL