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 之间的区别的主要内容,如果未能解决你的问题,请参考以下文章