serial 是 postgres 中很特殊的一个类型,与 mysql 中的 auto_increase 有些类似,
Usage
首先创建一个包含 Serial 的表
CREATE TABLE basic.tbl_serial (
id serial PRIMARY KEY,
ival int
)
从 pg_class 可以获得表的 oid
SELECT * FROM pg_class WHERE relname = 'tbl_serial';
-- oid | relname | relnamespace | reltype
-- -------+------------+--------------+---------
-- 16673 | tbl_serial | 16670 | 16675
-- (1 row)
根据 oid,从下面的 SQL 语句获得表的详细信息,从这里可以看到 serial 实际上就是设置了一下该字段的默认值,就是一个 sequence 的 nextval。
SELECT a.attname,
pg_catalog.format_type(a.atttypid, a.atttypmod),
(SELECT pg_catalog.pg_get_expr(d.adbin, d.adrelid, true)
FROM pg_catalog.pg_attrdef d
WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef),
a.attnotnull
FROM pg_catalog.pg_attribute a
WHERE a.attrelid = '16673' AND a.attnum > 0 AND NOT a.attisdropped
ORDER BY a.attnum;
-- attname | format_type | pg_get_expr | attnotnull
-- ---------+-------------+----------------------------------------------+------------
-- id | integer | nextval('basic.tbl_serial_id_seq'::regclass) | t
-- ival | integer | | f
-- (2 rows)
Sequence
sequence 就是一个生成器,用于生成整数,这个数一般会作为表的主键,来保证主键不会重复。 postgres 为 seqence 提供了一些函数。
currval(regclass) | 返回最近一次用 nextval 获取的指定序列的值 |
---|---|
lastval() | 返回最近一次用 nextval 获取的任何序列的值 |
nextval(regclass) | 递增序列并返回新值 |
setval(regclass, bigint) | 设置序列的当前值 |
setval(regclass, bigint, boolean) | 设置序列的当前值以及is_called标志 |
postgres 中所有 sequence 都存储在 pg_catalog.pg_sequence 中
SELECT * FROM pg_catalog.pg_sequence WHERE seqrelid = '16671';
-- seqrelid | seqtypid | seqstart | seqincrement | seqmax | seqmin | seqcache | seqcycle
-- ----------+----------+----------+--------------+------------+--------+----------+----------
-- 16671 | 23 | 1 | 1 | 2147483647 | 1 | 1 | f
-- (1 row)
可以看到对于默认创建的 sequence,它的起始值是 1,每次会增加 1,最大值是 2147483647,也就是 31 个 1,32 位有符号整数能够存储的最大值。
也可以在创建表的时候指定 serial 的类型
smallserial | 2 字节 | 自动增加 |
---|---|---|
serial | 4 字节 | 自动增加 |
bigserial | 8 字节 | 自动增加 |
Tips
如果在 INSERT
中添加条目的时候手动指定值,sequence 的值是不会改变的,再次添加还是按照原来的值,这可能会产生一些错误,比如下面这种情况,手动指定 id 添加下一个值之后,再使用默认值插入,就会插入失败,因为唯一索引值已经被占用了。
INSERT INTO basic.tbl_serial(id, ival) VALUES (2, 2);
INSERT INTO basic.tbl_serial(ival) VALUES (3);
-- ERROR: duplicate key value violates unique constraint "tbl_serial_pkey"
-- DETAIL: Key (id)=(2) already exists.
这个时候可以使用 sequence 的 setval
重新指定下一个值,或者手动再调用一次 nextval
,需要注意的是,在调用这些函数的时候需要参数需要使用 regclass。
SELECT setval('basic.tbl_serial_id_seq'::regclass, 2);
SELECT * FROM nextval('basic.tbl_serial_id_seq'::regclass);