跳到主要内容
版本:2.x

创建和管理序列

Apache Cloudberry 中的序列对象是一种特殊的单行表,用作数字生成器。可以使用序列为插入表中的行生成唯一的整数标识符。在表的某一列中声明 SERIAL 类型时,系统会自动创建一个对应的序列计数器。

Apache Cloudberry 提供了用于创建、修改和删除序列的命令,也提供了内置函数来获取序列的下一个值(nextval())或将序列设置为指定起始值(setval())。

注意

Apache Cloudberry 不支持 PostgreSQL 的 currval()lastval() 函数。

序列对象的属性包括序列名称、增量值、上次生成的值、最小值和最大值等。序列还有一个特殊的布尔属性 is_called,它决定了 nextval() 是否会在返回值之前对序列进行自增。当 is_calledtrue 时,nextval() 会先递增再返回;当 is_calledfalse 时,nextval() 直接返回当前值,不执行递增。

创建序列

CREATE SEQUENCE 命令用于创建并初始化一个序列,序列名必须在同一个 schema 中唯一,不能与已有的序列、表、索引或视图重名。可以指定可选的起始值。例如:

CREATE SEQUENCE myserial START 101;

新建序列时,系统会将其 is_called 属性设为 false。首次调用 nextval() 不会自增计数器,而是直接返回起始值,并将 is_called 设为 true

使用序列

通过 CREATE SEQUENCE 创建序列后,可以查看其属性,也可以使用内置函数进行操作。

查看序列属性

可以直接查询序列来查看其当前属性,例如查看名为 myserial 的序列:

SELECT * FROM myserial;

获取下一个序列值

调用内置函数 nextval() 可以获取并使用序列的下一个值。下面的示例将序列 myserial 的下一个值插入到表 vendors 的第一列中:

INSERT INTO vendors VALUES (nextval('myserial'), 'acme');

nextval() 会根据序列的 is_called 属性判断是否需要在返回前进行自增。如果 is_calledtrue,则先自增;并且在返回前会将 is_called 设置为 true

nextval() 的操作不会被事务回滚。即使执行 nextval() 的事务失败,获取到的值也会被视为已使用。这意味着失败的事务可能会在序列中留下未使用的空值。

注意

如果启用了镜像功能,则不能在 UPDATEDELETE 语句中使用 nextval() 函数。

设置序列计数器的值

可以使用 Apache Cloudberry 内置函数 setval() 设置序列的当前计数值。例如,以下命令将名为 myserial 的序列计数器设为 201

SELECT setval('myserial', 201);

setval() 有两种函数签名:setval(sequence, start_val)setval(sequence, start_val, is_called)。默认的 setval(sequence, start_val) 会将序列的 is_called 属性设为 true

如果希望下一次调用 nextval() 时不自动递增序列,可以使用带第三个参数的形式,将 is_called 设置为 false

SELECT setval('myserial', 201, false);

setval() 的执行不会被事务回滚。

使用 nextval() 和 setval() 时的注意事项

为了避免多个事务同时从同一序列获取值时发生阻塞,nextval() 获取的值不会因为事务回滚而被重用。因此,事务失败或数据库崩溃都可能导致生成的序列中出现空洞。有时即使事务没有失败,也可能出现空洞。例如,带有 ON CONFLICT 子句的 INSERT 会在判断是否存在冲突之前,就先计算要插入的元组,并执行 nextval()。因此,Apache Cloudberry 的序列不能保证生成连续的、不间断的数值。

同样,setval() 修改序列状态的操作对其他事务是立即可见的,即使当前事务最终回滚,这些更改也不会被撤销。

如果数据库集群在提交包含 nextval()setval() 的事务前发生崩溃,那么这些变更可能还没有写入持久存储。此时,重启集群后,序列的状态可能是修改前的,也可能是修改后的。这种情况不会影响数据库内部对序列的正常使用,因为未提交事务的其他效果也不会可见。但如果你打算将序列值用于数据库之外的持久用途,必须确保 nextval() 已经提交后再使用该值。

修改序列

ALTER SEQUENCE 命令用于修改已有序列的属性。可以调整序列的起始值、最小值、最大值和增量值,也可以重启序列计数。

未在 ALTER SEQUENCE 中指定的参数会保持原值不变。

使用 ALTER SEQUENCE sequence START WITH start_value 可以设置新的起始值,但不会影响当前的 last_value,也不会改变 nextval(sequence) 返回的值。

使用 ALTER SEQUENCE sequence RESTART 会将 last_value 重置为当前的起始值,并将 is_called 设为 false,下一次调用 nextval() 时会返回起始值。

使用 ALTER SEQUENCE sequence RESTART WITH restart_value 会将 last_value 设置为指定值,并将 is_called 设置为 false,下一次调用 nextval() 会返回该值。这相当于调用 setval(sequence, restart_value, false)

以下示例将名为 myserial 的序列从 105 开始重新计数:

ALTER SEQUENCE myserial RESTART WITH 105;

删除序列

使用 DROP SEQUENCE 命令可以删除一个序列。例如,以下命令会删除名为 myserial 的序列:

DROP SEQUENCE myserial;

将序列作为列的默认值

除了使用 SERIALBIGSERIAL 类型,也可以在 CREATE TABLE 命令中直接引用一个序列。例如:

CREATE TABLE tablename ( id INT4 DEFAULT nextval('myserial'), name text );

也可以使用 ALTER TABLE 修改某个列的默认值为序列计数器:

ALTER TABLE tablename ALTER COLUMN id SET DEFAULT nextval('myserial');

序列循环

默认情况下,序列不会循环。也就是说,当序列达到最大值后(SMALLSERIAL+32767SERIAL+2147483647BIGSERIAL+9223372036854775807),后续的每次 nextval() 调用都会报错。可以修改序列属性,让其循环计数,从 1 重新开始:

ALTER SEQUENCE myserial CYCLE;

也可以在创建序列时指定循环行为:

CREATE SEQUENCE myserial CYCLE;