13

我正在使用甲骨文

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

喜欢:

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有时会给出重复记录的错误...

请帮我解决这个疑问

4

4 回答 4

35

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

序列保证两个会话将获得不同的值,并且不需要序列化。它将表现得更好、更安全、更容易编码和更容易维护。

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

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

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

于 2013-06-20T10:13:02.233 回答
10

如果您打算拥有多个用户,则使用序列是可行的。使用 amax不会。

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

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

于 2013-06-20T10:14:02.550 回答
4

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

如果表中已经没有行,您的 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

于 2013-06-20T10:19:29.697 回答
0

假设 MAX(ID) 实际上足够快,是否有可能:

  • 首先得到 MAX(ID)+1
  • 然后得到 NEXTVAL
  • 比较这两个并增加序列以防 NEXTVAL 小于 MAX(ID)+1
  • 在 INSERT 语句中使用 NEXTVAL

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

于 2013-06-28T10:27:05.997 回答