我在这里遇到了一个奇怪的情况。我已经使用触发器和序列来实现自动增量。我从我的使用Hibernate的web应用程序中将数据插入到我的表中。我在我的机器(Netbeans)以及我的办公室网络上测试Web应用程序(Web应用程序也部署在我们的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中实现自动增量的正确方法是什么,以避免我面临的问题?在某些时候,我开始在主键上获得唯一键约束违规,因为(我不知道为什么原因)表中的max id恰好大于触发器中使用的sequence.nextval。造成这种情况的原因是什么以及如何解决?
答案 0 :(得分:1)
说实话,这篇文章本身很混乱。
你说明了,
“对于我的表xtable,其序列的last_number为78400,xtable中的最大id为78308,但序列的nextval为78304。”
它告诉我的是序列的最后一个数字是78400,有100个序列被缓存在内存中,必须在78300开始。一旦100个序列被缓存,它们只能被用作长时间因为服务器没有重新启动,它们会改变序列的最后一个值,以便在你的情况下显示78400,但这并不意味着已经使用了多少序列,这些序列只是缓存在内存中以供下一个插入使用的序列,除非数据库重新启动,在这种情况下,您将丢失缓存的序列号。 BTW序列缓存在不同会话之间共享。
“但是序列的nextval没有改变” 再次你假设它的序列的最后一个值与sequence.nextval相同它不是案件。当您查询dba_sequences视图并查看Last_NUMBER列时,它表示最后一个值CACHED,而不是sequence.nextval生成的最后一个值或在表中使用的值。
说实话,解决这个问题不应该花费太多精力。
一个。验证每次插入行时,必须使用序列而不是使用过程或触发器运行,然后返回序列,不要混合和匹配。 (记住在插入中使用直接序列的一个缺点是不保证顺序,因为可能有像1,2,3这样的条目用于id和next可能是10个原因可能是服务器重新启动并且您丢失了未使用的序列的缓存值,如果你真的总是想要订单而不是使用序列而不是使用程序或其他手段)。
B中。而不是首先查询表中的最大ID,然后删除序列,然后再次重新创建。
删除序列 first 然后从表中获取最大值,然后从该点开始创建序列。这将使您免于丢失可能已被其他会话中的脏事务使用的序列跟踪,这些事务可能在您进行查询以查找表上的最大ID时可能已提交....但它仍然不安全。
为了确保更好的结果,我只是创建新的序列,从下面查询显示的一个值以上的值开始,应该在删除序列之前使用。
select LAST_NUMBER from dba_sequences where sequence_name='YOUR_SEQUENCE_NAME'
基本上我所说的是安全地创建比当前缓存的值更大的新序列。
答案 1 :(得分:0)
我想到了我遇到这个问题的条件。问题是,当我加载成千上万条记录时,例如执行包含250000个插入查询的文件时,有人会尝试同时插入记录(通过我的webapp)。所以可能,当两个插入查询同时执行时会出现问题。