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 dWHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef),a.attnotnullFROM pg_catalog.pg_attribute aWHERE a.attrelid = '16673' AND a.attnum > 0 AND NOT a.attisdroppedORDER 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);
