可以依赖数据库中的自动递增主键吗?
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 字段用于此类任务,以免依赖数据库实现细节。
在命令执行期间生成 created 或 updated 字段对于稍后按创建时间或更新时间进行排序要好得多。 例如:
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 号是最后创建的。请记住,尽管数据库确实有能力暂时关闭自动生成值的插入,手动插入一些记录,然后再将其重新打开。这些插入通常不会在生产系统上使用,但在从另一个系统移动大量数据时偶尔会发生。
【讨论】:
以上是关于可以依赖数据库中的自动递增主键吗?的主要内容,如果未能解决你的问题,请参考以下文章