CURRENT_TIMESTAMP可以用作主键吗?


10

可以CURRENT_TIMESTAMP用作PRIMARY KEY

两个或更多不同的INSERT是否有可能获得相同的信息CURRENT_TIMESTAMP


3
我听说有个应用程序是在1990年代使用时间戳作为PK编码的。十年后,PC变得越来越快,并且时间戳被复制了。由于该应用程序的功能非常关键,因此造成了非常严重的问题。此外,在整个应用程序中未正确实施PK唯一性。
维克多·迪里奥

两个或更多不同的INSERT是否有可能获得相同的CURRENT_TIMESTAMP?一个查询插入2条记录以进行冲突就足够了。因此,主题问题的答案为“否”。
秋娜


3
我很好奇你为什么要这个?
Nanne

@Nanne我对此表示怀疑:MySQL对自动递增的整数id(仅是该字段的auto_increment属性)有很好的处理。PostgreSQL没有,它有一个串行类型,它的美观程度要低得多。
彼得-恢复莫妮卡

Answers:


18

根据文档,的精度CURRENT_TIMESTAMP为微秒。因此,碰撞的可能性低,但是可能。

现在想象这会发生的错误非常罕见,并导致数据库错误。调试有多难?它比至少具有确定性的错误要严重得多。

更广泛的上下文:您可能希望避免这些细微的差别,如果您习惯使用MySQL,这会特别烦人。

此外,如果您正在使用事务(大多数Web框架,尤其是Java框架都在使用!),那么事务中的时间戳将是相同的!演示:

postgres=# begin;
BEGIN
postgres=# select current_timestamp;
       current_timestamp       
-------------------------------
 2018-08-06 02:41:42.472163+02
(1 Zeile)

postgres=# select current_timestamp;
       current_timestamp       
-------------------------------
 2018-08-06 02:41:42.472163+02
(1 Zeile)

再见?两次选择,结果完全相同。我打得不太快。;-)

-

如果要轻松使用ID,避免使用序列,请从记录的真实标识符中生成一些哈希值。例如,如果您的数据库中有人类,并且您知道他们的出生日期,母亲的娘家姓和真实姓名唯一地标识了他们,则可以使用

md5(mother_name || '-' || given_name || '-' birthday);

作为ID。除此之外,您可以CreationDate在对表建立索引之后使用一列,但这不是键(即id)。

Ps通常,最好使数据库具有尽可能好的确定性。也就是说,相同的操作应该在数据库中创建完全相同的更改。任何基于时间戳的ID都会使此重要功能失效。如果您想调试或模拟任何东西怎么办?您重播一个操作,并且将使用不同的id创建相同的对象 ……这确实不难遵循,并且节省了大量工作时间。

Ps2出于上述原因,将来任何人检查您的代码时,看到时间戳生成的ID都不会有最好的意见。


即使您没有使用事务,您实际上也正在使用事务(因为Postgres不具有无事务处理模式,因此仅具有自动提交功能)。因此,如果您执行INSERT多个行中的一个,则它们都会得到相同的current_timestamp。然后您就有了触发器……
凯文(Kevin)

2
我听说有一个应用因为两个家伙名字相同并且出生在同一天而母亲名字相同而被破坏。哎哟。如果可以发生,那么迟早会发生。
Balazs Gunics,

@BalazsGunicsHelló:-)这只是一个例子。例如,在实际情况下,我认为将id作为电子邮件地址或所选的用户名(仅当它不存在时才可以注册)就足够了。政府倾向于使用一些个人识别号,例如1 8707280651。重要的是,将ID绑定到时间戳或随机值是不明智的做法,因为它会使DB的确定性降低。
peterh-恢复莫妮卡

@BalazsGunics除此之外,两个具有相同母亲名称+给定名称+生日的人,仍然会导致确定性错误。由于两个事务在同一微秒内发生了两次插入操作,因此主键冲突仍然是一个不确定的问题,并且很难重现。
peterh-恢复莫妮卡

10

并非如此,因为CURRENT_TIMESTAMP可以为两个后续的INSERT(或具有多个行的单个INSERT)提供两个相同的值。

请改用基于时间的UUID:uuid_generate_v1mc()


7

严格来说:否。因为CURRENT_TIMESTAMP是一个函数,所以只有一个或多个表可以构成PRIMARY KEY约束。

如果要PRIMARY KEY在具有默认值的列上创建约束CURRENT_TIMESTAMP,那么答案是:是的,可以。没有什么可以阻止您这样做,就像没有什么可以阻止您从儿子的头上射出苹果一样。当您不定义问题的目的时,该问题仍然没有意义。列和表应该保存哪种数据?您尝试执行哪些规则?

通常,由于CURRENT_TIMESTAMPSTABLE函数会为同一笔交易返回相同的值(交易的开始时间),因此必然会遇到重复的键错误。同一事务中的多个INSERT必然会发生冲突-就像已经说明的其他答案一样。手册:

由于这些函数返回当前事务的开始时间,因此它们的值在事务期间不会更改。这被认为是一个功能:目的是允许单个事务具有“当前”时间的一致概念,以便同一事务内的多个修改具有相同的时间戳。

Postgres时间戳实现为8字节整数,最多可表示6个小数位(微秒分辨率)。

如果您要构建的表每秒钟只能容纳不超过一行,并且该条件不会改变(称为sensor_reading_per_microsecond),可能有意义。重复的行应该引发重复的键冲突错误。不过,这是一个奇特的例外。而且,数据类型timestamptz(非timestamp)可能更可取。看到:

我还是宁愿使用替代串行主键来代替。并UNIQUE在时间戳列上添加一个约束。更少的麻烦,而不必依赖RDBMS的实现细节。


sensor_reading_per_microsecond如果您不能绝对保证每次阅读的时间都与上一次完全同步,甚至可能会发生冲突。亚微秒的偏差(通常并非不可能)破坏了方案。总的来说,我还是会完全避免这种情况。(请注意,在这种情况下,您可能会希望引起碰撞!)
Lightness Races in Orbit

@Lightness:我完全同意。您的示例在四舍五入的微小偏差后出现了意外的时移,这说明了另一个警告。
Erwin Brandstetter
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.