23

我有一个带有 SERIAL id 的 postgres 表。

id (serial) name age

插入通常发生在 Web 应用程序中。

我手动插入了两条新记录,将 id 设置为 max (id)+1 ****

在这些 2 插入之后,当 Web 应用程序插入 2 条记录时,它会给出重复键错误。

仅用于 2 条记录。之后一切正常。

问题是 - 为什么我的手动插入没有增加序列号?

自动增量和序列是不同的吗?

我在这里想念什么?MySQL 或任何其他 SQL 是否有相同的问题?

4

3 回答 3

54

当你创建一个serialorbigserial列时,PostgreSQL 实际上做了三件事:

  1. 创建一个intbigint列。
  2. 创建一个序列(由该列拥有)以生成该列的值。
  3. 将列的默认值设置为序列的nextval().

当您在未指定列的情况下插入值serial(或者如果您明确指定DEFAULT为其值)时,nextval将在序列上调用以:

  1. 返回列的下一个可用值。
  2. 增加序列的值。

如果您手动为该列提供一个非默认值,serial那么该序列将不会被更新并且nextval可以返回您的serial列已经使用的值。因此,如果您执行此类操作,则必须通过调用nextvalsetval手动修复序列。

另请记住,可以删除记录,因此serial可以预期列中的间隙,因此max(id) + 1即使没有并发问题,使用也不是一个好主意。

如果您使用serialor bigserial,最好的办法是让 PostgreSQL 为您分配值,并假装它们是碰巧以特定顺序出现的不透明数字:不要自己分配它们,不要'除了唯一性之外,不要假设它们的任何东西。此经验法则适用于所有数据库 IMO。


我不确定 MySQL 如何auto_increment与所有不同的数据库类型一起工作,但也许精美的手册会有所帮助。

于 2013-08-22T20:08:03.567 回答
8

如果您想在带有列的表中插入一条记录serial- 只需从查询中省略它 - 它将自动生成。

或者您可以使用以下内容插入其默认值:

insert into your_table(id, val)
values (default, '123');

第三种选择是直接从串行序列中取值:

insert into your_table(id, val)
values (nextval(pg_get_serial_sequence('your_table','id')), '123');
于 2013-08-22T20:11:13.140 回答
3

我手动插入了两条新记录,将 id 设置为 max (id)+1* *

这种方法是完全错误的,在任何数据库中都不起作用。到目前为止,它只在 MySQL 中对你有用,这完全是幸运的。

如果两个连接同时运行,它们将获得相同的 ID。只有锁定表以防止并发读取,它才能可靠地工作,这样一次只能有一个连接获取 ID。

它也非常低效。

这就是序列存在的原因,这样您就可以在存在并发插入器的情况下可靠地获取 ID。

只需使用:

INSERT INTO my_table(data1, data2) VALUES ('a','b') RETURNING id;

或者:

INSERT INTO my_table(id, data1, data2) VALUES (DEFAULT, 'a','b') RETURNING id;

DEFAULT是一个特殊的占位符,它告诉数据库从表定义中获取该列的默认值。默认值为nextval('my_table_id_seq'),因此将插入下一个序列值。

由于您要询问有关序列的基本问题,因此我建议您还考虑序列不是 gapless。序列有“洞”是正常的,其中表格值为 1、3、4、5、9、10,...。

于 2013-08-23T00:57:01.900 回答