Postgresql SERIAL 的工作方式是不是不同?

Posted

技术标签:

【中文标题】Postgresql SERIAL 的工作方式是不是不同?【英文标题】:Does Postgresql SERIAL work differently?Postgresql SERIAL 的工作方式是否不同? 【发布时间】:2013-08-25 17:17:27 【问题描述】:

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

id (serial) name age

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

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

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

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

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

自增和序列有区别吗?

我在这里缺少什么? mysql 或任何其他 SQL 是否有同样的问题?

【问题讨论】:

序列号是 PostgreSQL,这是一个自动递增的四字节整数,如果 id 是序列号,则不应生成自己的数字。在 MySQL 中,这或多或少是一个 AUTO_INCREMENT 列.. 【参考方案1】:

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

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

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

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

如果您手动为 serial 列提供非默认值,则序列不会更新,nextval 可以返回您的 serial 列已经使用的值。因此,如果您执行此类操作,则必须通过调用 nextval or setval 手动修复序列。

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

如果您使用 serialbigserial,最好的办法是让 PostgreSQL 为您分配值,并假装它们是碰巧按特定顺序出现的不透明数字:不要自己分配它们,也不要假设它们只有唯一性。此经验法则适用于所有数据库 IMO。


我不确定 MySQL 的 auto_increment 如何适用于所有不同的数据库类型,但也许 the fine manual 会有所帮助。

【讨论】:

谢谢。我认为mysql中没有额外的序列。它只是自动增量ID。所以 max(id)+1 可以在 mysql 中工作,但不能在 postgres 中工作! 但它应该有触发串行序列增量?你的建议是什么?我知道它不是那样的。只是提个建议 我的建议在我的回答中:将 serialauto_increment 值留给数据库。根本不要惹他们。如果您让数据库分配serial 值,那么序列将被更新,一切都会好起来的;如果您手动分配一个值,那么您必须手动更新序列。 @zod 手动插入 postgres 数据库不会触发对序列的任何更改。他们为什么要这样做?【参考方案2】:

如果您想将一条记录插入到带有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');

【讨论】:

谢谢...所以它与mysql不同..是吗? oracle和sql server中的顺序是一样的吗? @zod 在 Oracle 中也是一样,但我没有使用 MS SQL Server 的经验。 在未指定列时为 SERIAL 类型插入默认值的好答案。我一直在为 MySQL 中的 AUTO_INCREMENT 寻找 Postgres 的“null”插入等效项。谢谢。【参考方案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'),因此将插入下一个序列值。

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

【讨论】:

以上是关于Postgresql SERIAL 的工作方式是不是不同?的主要内容,如果未能解决你的问题,请参考以下文章

为啥这个查询这么慢? - PostgreSQL - 从 SERIAL、TIMESTAMP 和 NUMERIC(6,2) 中选择

如何使用 PostgreSQL 在 DBeaver 中创建自动递增/SERIAL id 列?

Postgresql ADD SERIAL COLUMN IF NOT EXISTS 仍在创建序列

RedShift / PostgreSQL 中串行类型的替代方案

PostgreSQL数据库 实现ID自增

PostgreSQL数据库 实现ID自增