【问题标题】:Oracle autoincrement with sequence and trigger issue具有序列和触发器问题的 Oracle 自动增量
【发布时间】:2016-10-17 13:58:20
【问题描述】:

我在这里遇到了一个奇怪的情况。我使用触发器和序列来实现自动增量。我从使用 Hibernate 的 Web 应用程序将数据插入到我的表中。我在我的机器 (Netbeans) 以及我的办公室网络上测试网络应用程序(网络应用程序也使用 Wildfly 部署在我们的服务器上)。

它一直运行良好,直到由于唯一约束(主键)而开始出现异常。然后我发现问题出在为 id 生成值的序列上。例如,对于我的表 xtable,它的序列的 last_number 是 78400,xtable 中的最大 id 是 78308,但是序列的 nextval 是 78304。我不知道这是怎么发生的,因为我创建了以下序列:

   CREATE SEQUENCE  XTABLE_SEQUENCE  INCREMENT BY 1 START WITH 1;

我尝试了以下方法来更新序列并使其 NEXTVAL 大于表中的 max(id),但在 n 次插入后我仍然得到相同的结果

declare
maxval number(10);
begin
   select max(ID) into maxval from XTABLE;
maxval := maxval+1;
execute immediate 'DROP SEQUENCE XTABLE_SEQUENCE';
execute immediate 'CREATE SEQUENCE XTABLE_SEQUENCE START WITH '|| maxval+50 ||' INCREMENT BY 1'; 
end;

这里是触发语句:

create or replace TRIGGER xtable_sequence_tr
BEFORE INSERT ON xtable FOR EACH ROW
WHEN (NEW.id IS NULL)
BEGIN
SELECT xtable_sequence.NEXTVAL INTO :NEW.id FROM DUAL;
END;

或者为了避免我面临的问题,在 Oracle 中实施自动增量的正确方法是什么?在某些时候,由于(我不知道出于什么原因)表中的最大 id 恰好大于触发器中使用的 sequence.nextval ,我开始在主键上违反唯一键约束。是什么原因造成的以及如何解决?

【问题讨论】:

  • 如果您重新创建序列但使用nocache,而不是cache 100,会发生什么情况?另外,您的数据库是 RAC 数据库吗?
  • 您提到了触发器。请向我们展示他们的内容。还有hibernate生成的sql语句
  • 你写了Its sequence's last_number is 78400, the max id in xtable is 78308, but the sequence's nextval is 78304. 但是Oracle序列不能回滚nextval值。你是手动改的吗?
  • @KonstantinSorokin 这里是hibernate Hibernate生成的sql语句:insert into ADMIN.XTABLE(COL0,COL1,COL2,COL3,COL4,COL5,ID)值(?,?,?,?, ?, ?, ?)
  • 您可以使用自治事务将触发器接收到的参数记录到日志表中。也许它从未发射过。

标签: java oracle triggers sequence auto-increment


【解决方案1】:

说实话,这篇文章本身就很令人困惑。

你说,

“对于我的表xtable,它的sequence的last_number是78400,xtable中的max id是78308,但是sequence的nextval是78304。”

  1. 它告诉我的是,通过将序列最后一个编号设置为 78400,有 100 个序列缓存在内存中,并且必须从 78300 开始。一旦缓存了 100 个序列,它们就只能使用由于服务器未重新启动,并且在您的情况下它们将序列最后一个值更改为显示 78400 但这并不意味着已经使用了多少序列,这些序列只是缓存在内存中以供下一次插入使用的序列,除非在这种情况下重新启动数据库,您将丢失那些被缓存的序列号。 BTW 序列缓存在不同的会话之间共享。

  2. “但序列的 nextval 没有改变” 你再次假设序列的 Last Value 与 sequence.nextval 相同,它不是案子。当您查询 dba_sequences 视图并查看 Last_NUMBER 列时,它表示最后一个值 CACHED,而不是由 sequence.nextval 生成或在表中使用的最后一个值。

老实说,解决这个问题并不需要太多努力。

A.验证每次插入行时都必须使用序列,而不是使用过程或触发器运行然后返回序列,不要混用和匹配。 (请记住,在插入中使用直接序列的一个缺点是不能保证顺序,例如 id 可能有 1、2、3 之类的条目,下一个可能是 10 原因可能是服务器重新启动并且您丢失了未使用的序列缓存值,如果你真的总是想要顺序而不是使用顺序而不是使用过程或其他方式)。

B.而不是首先查询表中的最大 id,然后删除序列,然后再次重新创建。

首先删除序列,然后从表中获取最大值,然后从该点开始创建序列。这将使您免于丢失可能已被其他会话中的脏事务使用的序列的跟踪,这些会话可能在您进行查询以查找表上的最大 id 时已提交....但它仍然不安全。

为了确保获得更好的结果,我将创建一个新序列,该新序列从高于下面查询显示的一个值开始,应该在删除序列之前使用它。

select LAST_NUMBER from dba_sequences where sequence_name='YOUR_SEQUENCE_NAME'

基本上,我要说的是安全地创建比当前缓存的值更大的新序列。

【讨论】:

  • 我所做的是,我创建了起始编号为 max(id) + 1000 的新序列。它暂时解决了问题,然后由于主键上的唯一约束违规,我的 webapp 再次开始显示错误消息列(如服务器日志中所示)。当我检查一个 sequence.nextval 时,它再次小于相关表上的 max(id)。
【解决方案2】:

我想出了我遇到这个问题的情况。问题是,当我加载数万条记录时,例如执行一个包含 250000 个插入查询的文件,有人会尝试同时插入记录(通过我的 web 应用程序)。所以可能是两个插入查询同时执行时出现的问题。

【讨论】:

  • 我有同样的问题,只是用户只更新表而不是插入记录。我发现最后一个序列在最大 ID 后面落后 20k 记录。我试图弄清楚如何用户更新导致了这样的问题。但在我的情况下,一个过程删除并添加记录,但仍然序列应该在插入时获得下一个值。
  • 这很棘手,我仍然遇到同样的问题,但仍然没有找出原因。这次发生的事情是,ids 1433 和 1434 已经被插入,然后抛出一个“唯一约束”,因为序列的 CURRVAL 是 1433 ,但应该是 1434,所以在下一次调用 NEXTVAL 时,它将是 1435 .
猜你喜欢
  • 1970-01-01
  • 2012-03-14
  • 2011-11-08
  • 1970-01-01
  • 2020-02-13
  • 2012-04-01
  • 2013-04-29
  • 2020-05-09
  • 1970-01-01
相关资源
最近更新 更多