PostgreSQL sequence

Published on:

PostgreSQL是非常流行的开源关系型数据库,在灵活的BSD License下发行。

其序列 sequence 是一个数据库对象,我们在 rails app 的数据库中新增 table 的时候,会默认生成一个对应的序列 table,并以其自增的数值作为 table 的主键(primary key)。

上图,除了 ar_internal_metadataschema_migration 这两张表以外,每一个 table 都会默认对应一个序列 table。

说道ar_internal_metadata,其是Rails 5默认添加的一个新的表来存储迁移数据库时使用的环境版本。
schema_migratio,是 Rails 用来记录数据库迁移版本的 table。

说回序列表,其就像其他 table一样,拥有自己的结构,只不过它的结构是固定的:

psql=# \d shipments_ship_id_seq
 Sequence "public.shipments_ship_id_seq"
Column     |  Type   |         Value
---------------+---------+-----------------------
 sequence_name | name    | shipments_ship_id_seq
 last_value    | bigint  | 0                
 start_value   | bigint  | 0
 increment_by  | bigint  | 1
 max_value     | bigint  | 9223372036854775807
 min_value     | bigint  | 0
 cache_value   | bigint  | 1
 log_cnt       | bigint  | 0
 is_cycled     | boolean | f
 is_called     | boolean | f    

table 每增加一笔记录,序列表的当前值就会自增1作为 table 新记录的主键,正常情况下,按照这种正常的逻辑,最后一笔记录的 id 值应该跟序列表的当前值是相等的。

不过,也有异常的时候,如果数据库紊乱或者被操作过,就有可能出现两者值不相等的情况,这时候,如果继续新增记录,就会出现报错,新纪录仍然会继续按照序列当前值递增作为主键 id 的值,而之前最后一笔记录的主键值却比这个值大,这时候,数据库就不知道该如何插入记录了,只好报错:key id already exists.

怎么解决呢?没错,最简单的办法就是把序列当前值设置为最后一笔记录的 id 值,让两者保持一致,就好了。

这就涉及到序列的函数使用:

nextval('sequence_name'): 将当前值设置成递增后的值,并返回
currval('sequence_name'): 返回当前值
setval('sequence_name', n, b=true): 设置当前值;b 默认设置 true,下一次调用 nextval() 时,直接返回 n,如果设置 false,则返回 n+increment:

其中,setval 函数可以拿来设置当前值。

psql=# SELECT setval('shipments_ship_id_seq', 1010);
 setval
--------
   1010
(1 row)

psql=# SELECT nextval('shipments_ship_id_seq');
 nextval
---------
    1011
(1 row)

假如当前最后一笔 users 记录的 id 为 29250

而我们数据库table 的序列表的当前值为 22530

那么我们只需要进入数据库,执行setval 函数,把当前值改成 29250就好了。


For more info, pl refer to link below:
PostgreSQL之序列(Sequence) - 码源-专注开源技术分享 - SegmentFault

Comments

comments powered by Disqus