在PostgreSQL中实现自增的三种方式

 更新时间:2026年04月28日 09:41:05   作者:hudson2022  
本文介绍了在 PostgreSQL 中创建自增列的三种方法:直接使用序列(sequence)、使用 serial 数据类型,以及使用 identity column 语法,文章通过实际示例详细讲解了每种方法的语法、行为和使用场景,需要的朋友可以参考下

摘要: 本文介绍了在 PostgreSQL 中创建自增列的三种方法:直接使用序列(sequence)、使用 serial 数据类型,以及使用 identity column 语法。文章通过实际示例详细讲解了每种方法的语法、行为和使用场景。

使用生成键(generated key)是数据库管理员(DBA)经常采用的做法,主要出于性能考虑。在理想情况下,我们本可以依赖表的自然主键就万事大吉。然而,使用人工主键已被证明对性能有益。

在这篇博客中,我不会解释什么是主键或什么是自然键/人工键。如果你对数据库设计感兴趣,可以找到很多优秀的书籍。

在这篇博客中,我将介绍在 Postgres 中实现自增的三种方式。

序列(Sequences)

实现自增数字的第一个明显方法是使用序列(sequence)。(实际上,我们稍后会看到其他方法也依赖于序列)。

以下是如何使用序列的简单示例:

laetitia=# create table test(id integeri primary key, value text);
CREATE TABLE
laetitia=# create sequence my_seq;
CREATE SEQUENCE
laetitia=# insert into test (select nextval('my_seq'), 'blabla');
INSERT 0 1
laetitia=# select * from test;
 id | value
----+--------
  1 | blabla
(1 row)

但我们可以做得更好。如果我们希望 id 列自动填充序列的下一个值呢?

laetitia=# create sequence my_seq;
CREATE SEQUENCE
laetitia=# create table test (id integer default nextval('my_seq') primary key, value text);
CREATE TABLE
laetitia=# insert into test(value) values ('blabla');
INSERT 0 1
laetitia=# select * from test;
 id | value
----+--------
  1 | blabla
(1 row)

以上就是使用 Postgres 实现自增 id 的第一种方法。

Serial 数据类型

自 Postgres 8.2(2006年发布)以来,Postgres 添加了 serial 数据类型。正如 Postgres 文档所述,它会做与我们之前相同的事情,但你只需用一个词就能完成。(参见 www.postgresql.org/docs/curren…

laetitia=# create table test (id serial primary key, value text);
CREATE TABLE
laetitia=# insert into test (value) values ('blabla');
INSERT 0 1
laetitia=# select * from test;
 id | value
----+--------
  1 | blabla
(1 row)

如果查看表结构,你会发现已经创建了一个序列,并用作 id 列的默认值:

laetitia=# \d test
                            Table "public.test"
 Column |  Type   | Collation | Nullable |             Default
--------+---------+-----------+----------+----------------------------------
 id     | integer |           | not null | nextval('test_id_seq'::regclass)
 value  | text    |           |          |
Indexes:
    "test_pkey" PRIMARY KEY, btree (id)
laetitia=# \ds
             List of relations
 Schema |    Name     |   Type   |  Owner
--------+-------------+----------+----------
 public | test_id_seq | sequence | laetitia
(1 row)

Identity 列

序列是符合 SQL 标准的。然而,serial 数据类型并不符合标准。但 SQL 有一种方法可以为列创建自增,而不需要显式创建序列。这就是 generated as identity

根据需求不同,该语法有两种形式。

第一种用法是当你想将序列值作为默认值,但允许手动输入其他值时。在这种情况下,语法是 generated by default as identity。但这不会阻止某人绕过序列直接插入值:

laetitia=# create table test (id integer generated by default as identity primary key, value text);
CREATE TABLE
laetitia=# \d test
                            Table "public.test"
 Column |  Type   | Collation | Nullable |             Default
--------+---------+-----------+----------+----------------------------------
 id     | integer |           | not null | generated by default as identity
 value  | text    |           |          |
Indexes:
    "test_pkey" PRIMARY KEY, btree (id)
laetitia=# insert into test (value) values ('blabla');
INSERT 0 1
laetitia=# select * from test;
 id | value
----+--------
  1 | blabla
(1 row)
laetitia=# insert into test (id, value) values (2,'blabla');
INSERT 0 1
laetitia=# select * from test;
 id | value
----+--------
  1 | blabla
  2 | blabla
(2 rows)
laetitia=# insert into test (value) values ('blabla');
ERROR:  duplicate key value violates unique constraint "test_pkey"
DETAIL:  Key (id)=(2) already exists.

如你所见,你可以手动添加值,然后由于序列滞后于 id 列中的最后一个数字,insert 操作会因重复键冲突而报错。

为了防止这种情况发生,我们可以创建一个约束来阻止任何人手动向该列插入数据。这就是另一种 generated as identity 语法。

laetitia=# create table test (id integer generated always as identity primary key, value text);
CREATE TABLE
laetitia=# \d test
                          Table "public.test"
 Column |  Type   | Collation | Nullable |           Default
--------+---------+-----------+----------+------------------------------
 id     | integer |           | not null | generated always as identity
 value  | text    |           |          |
Indexes:
    "test_pkey" PRIMARY KEY, btree (id)
laetitia=# insert into test (value) values ('blabla');
INSERT 0 1
laetitia=# insert into test (id, value) values (2,'blabla');
ERROR:  cannot insert a non-DEFAULT value into column "id"
DETAIL:  Column "id" is an identity column defined as GENERATED ALWAYS.
HINT:  Use OVERRIDING SYSTEM VALUE to override.

我们仍然可以检查底层是否使用了序列:

laetitia=# \ds
Did not find any relations.
laetitia=# create table test (id integer generated always as identity primary key, value text);
CREATE TABLE
laetitia=# \ds
             List of relations
 Schema |    Name     |   Type   |  Owner
--------+-------------+----------+----------
 public | test_id_seq | sequence | laetitia
(1 row)

总结一下,有几种方法可以为列创建自增值。我建议几乎在所有情况下都使用 generated always as identity,因为此语法会添加约束以防止因手动插入而导致的序列不同步。当然,如果有人想要篡改 identity 列背后的序列,只要有适当的权限,他们总能找到方法做到这一点。

特性序列(Sequence)SerialIdentity 列
自动使用 nextval 作为默认值
非空约束
阻止手动插入使用 always 时支持

到此这篇关于在PostgreSQL中实现自增的三种方式的文章就介绍到这了,更多相关PostgreSQL实现自增内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Postgresql 跨库同步表及postgres_fdw的用法说明

    Postgresql 跨库同步表及postgres_fdw的用法说明

    这篇文章主要介绍了Postgresql 跨库同步表及postgres_fdw的用法说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-01-01
  • PostgreSQL 行列转换的实现方法

    PostgreSQL 行列转换的实现方法

    本文主要介绍了PostgreSQL 行列转换的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-07-07
  • PostgreSQL如何选择合适的数据类型

    PostgreSQL如何选择合适的数据类型

    本文详细介绍了PostgreSQL中各种数据类型的特性、适用场景、潜在陷阱及最佳实践,涵盖了数值、字符、时间、布尔、枚举、网络、JSON、几何、全文搜索、范围、自定义类型等核心类别,并通过真实案例说明了数据类型选型的逻辑,感兴趣的朋友跟随小编一起看看吧
    2026-01-01
  • PostgreSQL VACUUM 清理机制详解

    PostgreSQL VACUUM 清理机制详解

    本文主要介绍了PostgreSQL VACUUM 清理机制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2026-05-05
  • PostgreSQL运维案例之递归查询死循环解决方案

    PostgreSQL运维案例之递归查询死循环解决方案

    PostgreSQL提供的递归语法是很棒的,例如可用来解决树形查询的问题,解决Oracle用户connect by的语法兼容性,下面这篇文章主要给大家介绍了关于PostgreSQL运维案例之递归查询死循环解决方案的相关资料,需要的朋友可以参考下
    2024-02-02
  • 关于PostgreSQL突然无法启动的排查过程及解决方法

    关于PostgreSQL突然无法启动的排查过程及解决方法

    SQL数据库服务器无法启动的原因可能有多种,这篇文章主要介绍了关于PostgreSQL突然无法启动的排查过程及解决方法,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2025-07-07
  • postgresql通过索引优化查询速度操作

    postgresql通过索引优化查询速度操作

    这篇文章主要介绍了postgresql通过索引优化查询速度操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • 关于向PostgreSQL数据库插入Date类型数据报错问题解决方案

    关于向PostgreSQL数据库插入Date类型数据报错问题解决方案

    本文给大家介绍在将数据库从Oracle改为PostgreSQL时遇到的日期类型插入错误,通过使用PostgreSQL的特定语法和更改动态SQL语句解决了问题,本文给大家介绍的非常详细,需要的朋友参考下吧
    2024-12-12
  • 使用postgresql 模拟批量数据插入的案例

    使用postgresql 模拟批量数据插入的案例

    这篇文章主要介绍了使用postgresql 模拟批量数据插入的案例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-01-01
  • PostgreSQL 查看服务器版本的三种方法

    PostgreSQL 查看服务器版本的三种方法

    这篇文章主要介绍了PostgreSQL 查看服务器版本的几种方法,本文给大家分享三种方法,结合实例代码给大家讲解的非常详细,需要的朋友可以参考下
    2023-03-03

最新评论