Oracle:max(id)+1 和 sequence.nextval 之间的区别

Posted

技术标签:

【中文标题】Oracle:max(id)+1 和 sequence.nextval 之间的区别【英文标题】:Oracle: difference between max(id)+1 and sequence.nextval 【发布时间】:2013-06-20 10:04:31 【问题描述】:

我正在使用甲骨文

当我们使用max(id)+1 和使用sequance.nexval 创建ID 时有什么区别,在哪里使用以及何时使用?

喜欢:

insert into student (id,name) values (select max(id)+1 from student, 'abc');

insert into student (id,name) values (SQ_STUDENT.nextval, 'abc');

SQ_STUDENT.nextval 有时会给出重复记录的错误...

请帮我解决这个问题

【问题讨论】:

【参考方案1】:

使用select max(id) + 1 方法,同时插入的两个会话将从表中看到相同的当前最大 ID,并且都插入相同的新 ID 值。安全使用它的唯一方法是在开始事务之前锁定表,这很痛苦并且会序列化事务。 (正如 Stijn 指出的那样,如果删除了最高记录,则可以重用值)。基本上,永远不要使用这种方法。 (可能偶尔会有令人信服的理由这样做,但我不确定我是否见过)。

sequence guarantees that the two sessions will get different values,不需要序列化。它将表现得更好、更安全、更容易编码和更容易维护。

使用序列获得重复错误的唯一方法是,如果表中已经存在 ID 高于序列值的记录,或者在不使用序列的情况下仍在插入记录。因此,如果您有一个带有手动输入 ID 的现有表,例如 1 到 10,并且您创建了一个默认起始值​​为 1 的序列,则使用该序列的第一个插入将尝试插入一个 ID 为 1 - 已经存在.在尝试了 10 次之后,序列会给你 11,这会起作用。如果您随后使用 max-ID 方法进行下一次插入,该插入将使用 12,但序列仍将在 11 上,并且在您下次调用 nextval 时也会给您 12。

序列和表格不相关。如果将手动生成的 ID 值插入表中,则不会自动更新序列,因此这两种方法不会混合使用。 (除其他外,可以使用相同的序列为多个表生成 ID,如文档中所述)。

如果您要从手动方法更改为序列方法,则需要确保创建的序列的起始值高于表中所有现有 ID,并且所有执行插入仅在将来使用该序列。

【讨论】:

【参考方案2】:

如果您打算拥有多个用户,则可以使用序列。使用max 不会。

如果您执行max(id) + 1 并允许多个用户,则同时运行的多个会话将定期看到相同的max,因此将生成相同的新密钥。假设您已经正确配置了约束,这将产生一个您必须处理的错误。您将通过重试INSERT 来处理它,如果其他会话在您的会话重试之前阻止您,这可能会一次又一次地失败,但对于每个INSERT 操作来说,这都是很多额外的代码。

它还会序列化您的代码。如果我在我的会话中插入一个新行并在我记得提交之前去吃午饭(或者我的客户端应用程序在我可以提交之前崩溃),那么每个其他用户将被阻止插入一个新行,直到我回来并提交或DBA 终止我的会话,强制重启。

【讨论】:

但有时为什么我使用 sequence.nextval 出现重复记录错误,并且当我使用 max(id)+1 运行相同的查询时它可以工作 @PriyaPrajapati - 因为您的序列的当前值低于表中的最大 ID。如果您在表中手动插入值,则不会修改序列。您不应该混合使用这两种方法 - 获取高于表值的序列,然后仅使用序列。【参考方案3】:

添加到其他答案,几个问题。

如果表中已经没有行,你的 max(id)+1 语法也会失败,所以使用:

Coalesce(Max(id),0) + 1

如果您只有一个插入到表中的进程,这种技术没有任何问题,就像数据仓库负载的情况一样,并且如果 max(id) 很快(很可能是这样)。

例如,如果您将恢复数据移动到测试系统,它还避免了代码在表和序列之间同步值的需要。

您可以使用以下方法将此方法扩展到多行插入:

Coalesce(max(id),0) + rownum

不过,我希望这可能会串行化并行插入。

某些技术不适用于这些方法。它们当然依赖于能够发出 select 语句,因此 SQL*Loader 可能被排除在外。但是 SQL*Loader 通常通过列规范的 SEQUENCE 参数支持这种技术:http://docs.oracle.com/cd/E11882_01/server.112/e22490/ldr_field_list.htm#i1008234

【讨论】:

对此添加了注释,并扩展了对 SQL*Loader 的注释。【参考方案4】:

假设 MAX(ID) 实际上足够快,是不是可以:

首先获取 MAX(ID)+1 然后获取 NEXTVAL 比较这两者并增加序列,以防 NEXTVAL 小于 MAX(ID)+1 在 INSERT 语句中使用 NEXTVAL

在这种情况下,我将拥有一个完全稳定的过程,并且也允许手动插入,而不必担心更新序列

【讨论】:

您正在引入差距,这并不是真正的问题;并且您仍然有一个窗口,其中两个会话查询相同的 max(id) 值,尽管 nextval 调用应该使它工作。但是,您不能用普通的 SQL 来执行此操作,并且允许混合仍然很麻烦。另一种类似的方法是尝试在循环中使用nextval 插入,如果您没有 得到ORA-00001,则退出,假设列上有PK/UK。这避免了寻找最大值并填补空白,这可能是可取的,也可能不是可取的。不过,只有使用序列更容易......

以上是关于Oracle:max(id)+1 和 sequence.nextval 之间的区别的主要内容,如果未能解决你的问题,请参考以下文章

max id 的日期:sql/oracle 优化

在oracle中where 子句和having子句中的区别

Oracle-在不存在的地方插入select max

如何通过 Oracle 中的 SQL 查询找到每个 id 的 TOP/MAX 值?

如何在 Oracle 中重置序列?

用oracle怎样查询每个部门的部门编号,部门名称,平均工资,最高工资和最低工资