将 Oracle 序列增加一定数量

Posted

技术标签:

【中文标题】将 Oracle 序列增加一定数量【英文标题】:Incrementing Oracle Sequence by certain amount 【发布时间】:2011-07-01 13:30:36 【问题描述】:

我正在编写一个 Windows 应用程序(在 Qt 4.6 中),它 - 在某些时候 - 将 1 到 76000 之间的任意数量的数据集插入到某个 oracle (10.2) 表中。应用程序必须从序列中检索主键,或者至少是主键范围。然后它将 ID 存储在一个列表中,用于批量执行准备好的查询。

(注意:不能使用触发器,其他任务也使用该序列)

为了避免调用序列X次,我想将序列增加X。

到目前为止,我发现以下代码可以在一个过程中使用:

ALTER SEQUENCE my_sequence INCREMENT BY X;

SELECT my_sequence.CURVAL + 1, my_sequence.NEXTVAL
INTO   v_first_number, v_last_number
FROM   dual;

ALTER SEQUENCE my_sequence INCREMENT BY 1;

不过,我有两个主要顾虑:

    我读到 ALTER SEQUENCE 会产生一个隐式提交。这是否意味着将提交由 Windows 应用程序启动的事务?如果是这样,你能以某种方式避免它吗?

    这个概念是多用户证明吗?或者会发生以下情况:

    Sequence is at 10,000
    Session A sets increment to 2,000
    Session A selects 10,001 as first and 12,000 as last
    Session B sets increment to 5,000
    Session A sets increment to 1
    Session B selects 12,001 as first and 12,001 as last
    Session B sets increment to 1
    

    即使该过程会相当快,但在我的应用程序中,两个不同的用户导致该过程几乎同时被调用的可能性不大

【问题讨论】:

为什么不让数据库只管理递增1的序列呢?在插入语句中使用 Seq.NEXTVAL。这不是性能问题。 我正在使用一个准备好的查询,它接收列表而不是单个值作为绑定值,并通过 Qt 的“execBatch()”命令执行。我不知道在批处理执行的准备好的查询中使用 seq.NEXTVAL 是否有效,周一必须尝试一下。然而,考虑到性能,批量执行是必须的。 【参考方案1】:

1) ALTER SEQUENCE 是 DDL,因此它在语句之前和之后隐式提交。 Windows 应用程序启动的数据库事务将被提交。如果您使用的是 Oracle 数据库以外的分布式事务协调器,希望事务协调器将提交整个分布式事务,但事务协调器有时会遇到它不知道的提交问题。 您无法阻止 DDL 提交。

2) 您概述的多个用户的场景是很有可能的。因此,听起来这种方法在您的环境中表现不佳。

您可能会使用DBMS_LOCK 包来确保在任何时间点只有一个会话正在调用您的过程,然后从单个 SQL 语句中调用序列 N 次。但如果其他进程也使用该序列,则无法保证您将获得一组连续的值。

CREATE PROCEDURE some_proc( p_num_rows IN NUMBER,
                            p_first_val OUT NUMBER,
                            p_last_val  OUT NUMBER )
AS
  l_lockhandle       VARCHAR2(128);
  l_lock_return_code INTEGER;
BEGIN
  dbms_lock.allocate_unique( 'SOME_PROC_LOCK',
                             l_lockhandle );
  l_lock_return_code := dbms_lock.request( lockhandle => l_lockhandle,
                                           lockmode => dbms_lock.x_mode,
                                           release_on_commit => true );
  if( l_lock_return_code IN (0, 4) ) -- Success or already owned
  then
    <<do something>>
  end if;

  dbms_lock.release( l_lockhandle );
END; 

【讨论】:

我很确定这个解决方案会有所帮助,但只需在查询中使用 sequence.nextval 就可以了,所以我没有尝试这个。无论如何,谢谢,也许它可以帮助其他偶然发现这个话题的人!【参考方案2】:

在这种情况下更改顺序确实是个坏主意。特别是在多用户环境中。您将提交您的事务,并且可能会出现几个“竞争条件”数据错误或完整性错误。 如果您已经导入了遗留数据并希望插入带有序列中 id 的新数据,这将是合适的。然后您可以更改序列以将 currval 移动到最大现有...

在我看来,您想在这里从序列中生成 Id。这不需要由

完成
select seq.nextval into l_variable from dual;
insert into table (id, ...) values (l_variable, ....);

可以直接在插入中使用序列:

insert into table values (id, ...) values (seq.nextval, ....);

并可选择通过

取回分配的值
insert into table values (id, ...) values (seq.nextval, ....)
returning id into l_variable;

当然,即使是使用 execBatch 进行批量操作也是可能的。要么只是创建 ID,甚至返回它们。我不确定java中的正确语法,但它会是关于行的东西

insert into table values (id, ...) values (seq.nextval, ....)
returning id bulk collect into l_cursor;

您将获得一个 ResultSet 来浏览分配的数字。

【讨论】:

我不需要在将 ID 插入表后检索它们,但直接在值部分中使用 seq.nextval 甚至可以使用 execBatch。我无法在星期一之前对其进行测试,但我会报告它是否有效。 在查询中使用 seq.nextval 就可以了!起初我的 execBatch() 执行时间增加了三倍(从 10 秒到大约 30 秒),因为序列没有缓存,但是使用缓存值,我现在只降低了大约 5% 的执行时间(与生成连续我的 Windows 应用程序中的值)没关系。谢谢!【参考方案3】:

    您无法阻止隐式提交。

    您的解决方案不是多用户证明。正如您所描述的那样,另一个会话完全有可能将增量“恢复”为 1。

我建议您继续从序列中一个一个地获取值,将这些 ID 一个一个地存储在您的列表中,并在该列表上执行批处理。

您想从序列中获取连续的值块的原因是什么?我不会太担心性能,但可能还有其他我不知道的要求。

【讨论】:

通过我的程序从序列中一个一个地获取 70.000 个 ID 需要 80 秒。到目前为止,这是不可接受的。 哎哟。从 PL/SQL 中的序列中获取 70000 个 id 需要大约 4 秒的时间在我手头的数据库上。这是很多开销。【参考方案4】:

在 Oracle 中,您可以使用以下查询从递增 1 的序列中获取下一个 N 个值:

选择级别,PDQ_ACT_COMB_SEQ.nextval as seq from dual connect by level

【讨论】:

以上是关于将 Oracle 序列增加一定数量的主要内容,如果未能解决你的问题,请参考以下文章

最长增加子序列的数量[重复]

计算滑块在一定数量后增加

关于oracle索引数量

java项目连接oracle数据库,连的人多操作数据库会越来越慢,怎么解决

MySQL (NOT IN) 条件在超过一定数量的元素后无法正确返回

单据体增加合计数量