可以依赖数据库中的自动递增主键吗?

Posted

技术标签:

【中文标题】可以依赖数据库中的自动递增主键吗?【英文标题】:Can one rely on the auto-incrementing primary key in your database? 【发布时间】:2010-10-25 03:05:58 【问题描述】:

在我目前的 Rails 应用程序中,我通过“created_at”字段对模型进行排序来解决调度冲突。但是,我意识到,当从允许这样做的表单中插入多个模型时,所有 created_at 时间都完全相同!

这更多是最佳编程实践的问题:您的应用程序能否依靠数据库中的 ID 列随着每个 INSERT 越来越大地递增以获得它们的创建顺序?换句话说,我可以按 ID 列对从数据库中提取的一组行进行排序,并确保这是基于创建顺序的准确排序吗?这在我的应用程序中是一个好的做法吗?

【问题讨论】:

【参考方案1】:

生成的标识号将是唯一的。 无论您是使用 PostgreSQL 和 Oracle 中的序列,还是使用 mysql 的自动增量等其他机制。

但是,序列通常是批量获取的,例如 20 个数字。 因此,使用 PostgreSQL,您无法确定首先插入了哪个字段。插入记录的 id 甚至可能存在间隙。

因此,您不应将生成的 id 字段用于此类任务,以免依赖数据库实现细节。

在命令执行期间生成 createdupdated 字段对于稍后按创建时间或更新时间进行排序要好得多。 例如:

INSERT INTO A (data, created) VALUES (smething, DATE())
UPDATE A SET data=something, updated=DATE()

【讨论】:

【参考方案2】:

这取决于您的数据库供应商。

我相信 MySQL 绝对会订购自动增量键。 SQL Server 我不确定它是否存在,但我相信它存在。

您会遇到问题的地方是不支持此功能的数据库,尤其是使用大致但不是绝对有序的序列的 Oracle。

另一种方法可能是先创建时间,然后是 ID。

【讨论】:

我喜欢冗余的想法,按一个排序,然后使用 ID 进行二次排序。 . .谢谢! Oracle 序列是为非 RAC 安装订购的,这是其中的绝大多数。【参考方案3】:

我相信您的问题的答案是肯定的...如果我在字里行间阅读,我认为您担心系统可能会重复使用序列中“缺失”的 ID 号码,因此如果您有使用 1,2,3,5,6,7 作为 ID 号,在我知道的所有实现中,下一个 ID 号将始终为 8(或可能更高),但我不知道有任何数据库会尝试并找出记录 ID #4 丢失,因此尝试重新使用该 ID 号。

虽然我最熟悉 SQL Server,但我不知道为什么任何供应商会尝试按顺序填补空白 - 想想保留未使用 ID 列表的开销,而不是始终跟踪最后使用的 I 数,加 1。

我想说你可以放心地依赖下一个分配的 ID 编号总是高于上一个 - 不仅仅是唯一的。

【讨论】:

【参考方案4】:

是的,id 将是唯一的,不,您不能也不应该依赖它进行排序 - 它只是为了保证行的唯一性。正如 emkta 所指出的,最好的方法是使用单独的“更新”或“创建”字段来存储这些信息。

要设置创建时间,你可以像这样使用默认值

CREATE TABLE foo (
  id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL;
  created TIMESTAMP NOT NULL DEFAULT NOW();
  updated TIMESTAMP;
  PRIMARY KEY(id);
) engine=InnoDB; ## whatever :P

现在,它负责创建时间。对于更新时间,我建议使用像这样的 AFTER UPDATE 触发器(当然您可以在单独的查询中执行此操作,但在我看来,触发器是一个更好的解决方案 - 更透明):

DELIMITER $$
CREATE TRIGGER foo_a_upd AFTER UPDATE ON foo
FOR EACH ROW BEGIN
  SET NEW.updated = NOW();
END;
$$
DELIMITER ;

应该这样做。

编辑: 祸是我。愚蠢的是我没有指定,这是针对 mysql 的,函数名称(即 'NOW')和其他细微之处可能存在一些差异。

【讨论】:

你忽略了 OP 所说的:他已经有一个 created_at 字段,并且同时插入多个项目时它是相同的,因此他的问题。 确实,我的思路,尽管如此疯狂地转向,似乎已经偏离了轨道。对不起。你当然是对的。首先按 created_at 然后按 id 排序可能是最简单的解决方案(尽管它可能并不严格保证正确的顺序,它只保证具有相同 created_at 值的行将以相同的顺序返回每个时间)。【参考方案5】:

对 EJB 回答的一个警告:

如果您不按列指定顺序,SQL 不提供任何顺序保证。例如。如果你删除一些早期的行,然后插入它们,新的可能最终会在旧的 db 中的同一个位置(尽管有新的 ID),这就是它可能用作默认排序的地方。

FWIW,我通常使用 order by ID 作为 order by created_at 的有效版本。它更便宜,因为它不需要向 datetime 字段添加索引(它比简单的整数主键索引更大,因此比简单的整数主键索引更慢),保证不同,而且我真的不在乎是否有几行大约在同一时间添加,排序略有不同。

【讨论】:

【参考方案6】:

这可能取决于数据库引擎。我会检查你的数据库是如何实现序列的,如果没有记录的问题,那么我会决定依赖 ID。

例如Postgresql sequence 可以,除非您使用序列缓存参数。

其他程序员可能会手动创建或复制来自不同数据库的错误 ID 列的记录。但是我会简化问题。不要担心有人会手动破坏数据完整性的低概率情况。你无法防范一切。

我的建议是依靠序列生成的 ID 并推进您的项目。

【讨论】:

【参考方案7】:

理论上是的,最高的 id 号是最后创建的。请记住,尽管数据库确实有能力暂时关闭自动生成值的插入,手动插入一些记录,然后再将其重新打开。这些插入通常不会在生产系统上使用,但在从另一个系统移动大量数据时偶尔会发生。

【讨论】:

以上是关于可以依赖数据库中的自动递增主键吗?的主要内容,如果未能解决你的问题,请参考以下文章

在单个 INSERT 查询中插入多行会保证顺序自动递增主键吗?

SQL中的每一张表都必须设有主键吗

多对多表应该有一个主键吗?

SQL中的每一张表都必须设有主键吗

数据库表可以没有主键吗?

mysql表中一个表中可以有多个主键吗?