问题
如何在回滚的情况下使用Oracle DB序列而不丢失下一个序列号?
收集的事实
1 - 在Oracle中,我们可以创建一个序列并使用两个主要调用(NEXTVAL
)来获取下一个序列值,并使用(CURRVAL
)来获取当前序列值。
2 - 当我们打电话时(NEXTVAL
)将始终获得下一个号码,如果有回滚,我们将丢失它。换句话说,Oracle序列不关心是否存在回滚或提交;无论何时你打电话,它都会给你一个新号码。
到目前为止我找到的可能答案
1 - 我正在考虑创建一个包含一列类型(NUMBER)的简单表来为此目的服务。 只需选择值并使用它。如果操作成功,我将增加列值。否则,我将保留原样用于下一次应用程序调用。
2 - 我在这里找到的另一种方式(How do I reset a sequence in Oracle?)是使用(ALTER SEQUENCE
)如下所示,如果我想退后一步。
也就是说,如果序列是101,我可以通过
将其设置为100ALTER SEQUENCE serial INCREMENT BY -1;
SELECT serial.NEXTVAL FROM dual;
ALTER SEQUENCE serial INCREMENT BY 1;
结论
是否有任何建议的解决方案?他们有更好的方法吗?
答案 0 :(得分:2)
从我的观点来看,你应该使用一个序列并且不要担心差距。
从你的角度来看,我会说改变序列更糟比拥有一张桌子。请注意,对该表的访问必须限制为单个用户,否则如果两个(或更多)用户同时访问它,您将获得重复值。
这是一个示例代码;看看,如果你愿意,可以使用/调整它。
SQL> create table broj (redni_br number not null);
Table created.
SQL>
SQL> create or replace function f_get_broj
2 return number
3 is
4 pragma autonomous_transaction;
5 l_redni_br broj.redni_br%type;
6 begin
7 select b.redni_br + 1
8 into l_redni_br
9 from broj b
10 for update of b.redni_br;
11
12 update broj b
13 set b.redni_br = l_redni_br;
14
15 commit;
16 return (l_redni_br);
17 exception
18 when no_data_found
19 then
20 lock table broj in exclusive mode;
21
22 insert into broj (redni_br)
23 values (1);
24
25 commit;
26 return (1);
27 end f_get_broj;
28 /
Function created.
SQL> select f_get_broj from dual;
F_GET_BROJ
----------
1
SQL> select f_get_broj from dual;
F_GET_BROJ
----------
2
SQL>
答案 1 :(得分:1)
您可以创建序列表。
CREATE TABLE SEQUENCE_TABLE
(SEQUENCE_ID NUMBER,
SEQUENCE_NAME VARCHAR2(30 BYTE),
LAST_SEQ_NO NUMBER);
在PL / SQL块中,您可以使用下面的代码行
来获取序列declare
CURSOR c1 IS
SELECT last_seq_no
FROM sequence_table
WHERE sequence_id = 21
FOR UPDATE NOWAIT;
v_last_seq_no NUMBER;
v_new_seq_no NUMBER;
resource_busy EXCEPTION;
PRAGMA EXCEPTION_INIT(resource_busy, -54);
BEGIN
LOOP
BEGIN
OPEN c1;
FETCH c1 INTO v_last_seq_no;
CLOSE c1;
v_new_seq_no := v_last_seq_no+1;
EXIT;
EXCEPTION
WHEN resource_busy THEN
NULL;
--or something you want to happen
END;
END LOOP;
--after the this line, you can put an update to the sequence table and be sure to commit/rollback at the end of the pl/sql block;
END;
/
ROLLBACK;
--or
COMMIT;
尝试在两个oracle会话中运行上面的PL / SQL代码来理解。基本上,如果Oracle DB会话1将运行代码,则从光标查询的记录将被锁定。因此,如果其他会话将运行相同的代码,该会话将等待会话1的回滚/提交以完成代码运行。通过这个,两个会话不会有相同的sequence_no,如果由于某些原因发出回滚,你可以选择不更新序列。