postgres中的serial和identity的使用

2024-09-19 15:20:46 浏览数 (4)

原文地址 https://www.naiyerasif.com/post/2024/09/04/stop-using-serial-in-postgres/

从PG10开始支持identity,用于替代serial。

part1、serial有权限问题

代码语言:txt复制
	想象一下:数据库所有者victoria创建如下表:
	postgres=# create table events (
		id serial primary key,
		created_at timestamptz not null default current_timestamp
	);
	
	然后,他们将events表的所有权限授予另一个用户gizem。但是当gizem尝试插入或更新一行时,他们收到此错误:
	postgres=# insert into events default values;
	-- ERROR: permission denied for sequence events_id_seq
	
	解决办法?只需授予生成id列的序列的使用权限即可:
	postgres=# grant usage on sequence events_id_seq to gizem;
	或者,切换到标识列。如果最后一个id值为 99,则操作如下:
	postgres=# drop sequence events_id_seq cascade;
	postgres=# alter table events alter column id add generated always as identity (restart 100);

part2、serial缺乏完整性保证

代码语言:txt复制
	postgres=# create table pings (
		id serial primary key,
		last_ping timestamptz not null default current_timestamp
	);

	postgres=# d pings
										  Table "public.pings"
	  Column   |           Type           | Collation | Nullable |              Default              
	----------- -------------------------- ----------- ---------- -----------------------------------
	 id        | integer                  |           | not null | nextval('pings_id_seq'::regclass)
	 last_ping | timestamp with time zone |           | not null | CURRENT_TIMESTAMP
	Indexes:
		"pings_pkey" PRIMARY KEY, btree (id)

	插入一行:
	insert into pings values (1, default);
	
	该行被添加id为 1。现在尝试这样做:
	insert into pings default values;
	-- ERROR: duplicate key value violates unique constraint "pings_pkey"
	-- DETAIL: Key (id)=(1) already exists.
	
	发生了什么?第一次插入id1 并没有推进序列。Postgres 没有发出任何警告或错误。

	现在,让我们用identity尝试做同样的事情:
	postgres=# create table pings2 (
		id int generated always as identity primary key,
		last_ping timestamptz not null default current_timestamp
	);

	postgres=# d pings2
									   Table "public.pings2"
	  Column   |           Type           | Collation | Nullable |           Default            
	----------- -------------------------- ----------- ---------- ------------------------------
	 id        | integer                  |           | not null | generated always as identity
	 last_ping | timestamp with time zone |           | not null | CURRENT_TIMESTAMP
	Indexes:
		"pings2_pkey" PRIMARY KEY, btree (id)
	
	postgres=# d pings2_id_seq 
					   Sequence "public.pings2_id_seq"
	  Type   | Start | Minimum |  Maximum   | Increment | Cycles? | Cache 
	--------- ------- --------- ------------ ----------- --------- -------
	 integer |     1 |       1 | 2147483647 |         1 | no      |     1
	Sequence for identity column: public.pings2.id

	postgres=# insert into pings2 values (1, now());
	-- 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.
	这次您会收到一个友好的错误,解释插入失败的原因。如果要插入可以带上 OVERRIDING SYSTEM VALUE ,例如:
	INSERT INTO pings2 (id,last_ping) OVERRIDING SYSTEM VALUE  VALUES(1,now());
  

	此外,假如当您误操作将pings_id_seq删除的时候:
	postgres=# drop sequence pings_id_seq cascade;
	-- NOTICE: drop cascades to default value for column id of table pings
	Postgres 只会给你一个通知,但不会阻止你。因此,您最终会得到default从id列。可以d查看到表结构,自增序列没有了
	
	postgres=# d pings
								  Table "public.pings"
	  Column   |           Type           | Collation | Nullable |      Default      
	----------- -------------------------- ----------- ---------- -------------------
	 id        | integer                  |           | not null | 
	 last_ping | timestamp with time zone |           | not null | CURRENT_TIMESTAMP
	Indexes:
		"pings_pkey" PRIMARY KEY, btree (id)
	
	这可能不是你想要的主键上的东西。更糟糕的是,您无法恢复id列返回到serial.相反,您需要创建一个新序列,并使用
	alter table ... set default.这真是个麻烦!

	现在,让我们用 identity 列来尝试一下。
	postgres=# drop sequence pings2_id_seq cascade;
	ERROR:  cannot drop sequence pings2_id_seq because column id of table pings2 requires it
	HINT:  You can drop column id of table pings2 instead.
	可以看到有个error报错,提示在使用中,禁止被删除。

part3、serial不是真正的类型,serial当您创建一个包含如下列的表时:

代码语言:txt复制
	create table events (
		id serial primary key,
		created_at timestamptz not null default current_timestamp
	);
	
	Postgres 将其解析为下面3条sql:
	1、 create sequence events_id_seq as integer;
	2、 create table events (
		id integer primary key not null default nextval('events_id_seq'::regclass),
		created_at timestamp with time zone not null default current_timestamp
	);
	3、 alter sequence events_id_seq owned by events.id;

part4、identity和serial在设置id的时候使用方法类似

代码语言:txt复制
	serial的调整方法:
		alter sequence events_id_seq restart 100;
	identity的调整方法:
		alter sequence events2_id_seq restart with 300; 或 alter table events2 alter column id restart with 100;

part5、identity和serial在复制表时候的现象不一样

代码语言:txt复制
	postgres=# create table return_events (like events including all);
	postgres=# d return_events
									   Table "public.return_events"
	   Column   |           Type           | Collation | Nullable |              Default               
	------------ -------------------------- ----------- ---------- ------------------------------------
	 id         | integer                  |           | not null | nextval('events_id_seq'::regclass)
	 created_at | timestamp with time zone |           | not null | CURRENT_TIMESTAMP
	Indexes:
		"return_events_pkey" PRIMARY KEY, btree (id)
	可以看到 return_events 表的序列用的是 events_id_seq (和events表共用一个sequence)

	postgres=# create table pings2new (like pings2 including all);
	postgres=# d pings2new
									  Table "public.pings2new"
	  Column   |           Type           | Collation | Nullable |           Default            
	----------- -------------------------- ----------- ---------- ------------------------------
	 id        | integer                  |           | not null | generated always as identity
	 last_ping | timestamp with time zone |           | not null | CURRENT_TIMESTAMP
	Indexes:
		"pings2new_pkey" PRIMARY KEY, btree (id)
		
	postgres=# ds
					List of relations
	 Schema |       Name       |   Type   |  Owner   
	-------- ------------------ ---------- ----------
	 public | events2_id_seq   | sequence | postgres
	 public | events_id_seq    | sequence | postgres
	 public | pings2_id_seq    | sequence | postgres
	 public | pings2new_id_seq | sequence | postgres
	(4 rows)
	
	可以看到 pings2new 表的序列用的是 pings2new_id_seq (和pings2表不会共用一个sequence)

0 人点赞