一个生成列
是一个始终根据其他列计算得出的特殊列。
生成列是在PostgreSQL 12中引入的。
功能
截至PostgreSQL 12,生成列只能为 STORED
类型。提交 fc22b662 指示另一种类型的生成列 VIRTUAL
可能会在将来某个日期添加。
从PostgreSQL 13 开始,可以使用 ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION
命令将生成列转换为普通列。
从PostgreSQL 17 开始,可以更改用于创建生成列的表达式。
实现
attgenerated
列已添加到 pg_attribute
中,以指示该列是否为生成列;s
表示该列为 STORED
类型,否则该值为为空,并且该列不是生成列。
外部数据包装器 (FDW) 支持
外部表 可以使用生成列定义,并且生成的值将提供给底层的 外部数据包装器。FDW 应确保将值存储在外部服务器上,并且可以随后检索,但是不能保证检索到的值不会在外部服务器上更改。
更改历史记录
- PostgreSQL 17
- 可以更改生成列的生成表达式(提交 5d06e99a)
- PostgreSQL 13
- 可以将生成列转换为普通列(提交 f595117e)
- PostgreSQL 12
- 添加(提交 fc22b662)
示例
带生成列的表的简单示例,此处用于预先计算过时的温度转换
postgres=# CREATE TABLE temperature ( celsius SMALLINT NOT NULL, fahrenheit SMALLINT NOT NULL GENERATED ALWAYS AS ((celsius * 9/5) + 32) STORED ); CREATE TABLE postgres=# \d temperature Table "public.temperature" Column | Type | Collation | Nullable | Default ------------+----------+-----------+----------+----------------------------------------------------- celsius | smallint | | not null | fahrenheit | smallint | | not null | generated always as ((celsius * 9 / 5 + 32)) stored
插入数据
postgres=# INSERT INTO temperature VALUES (0), (100); INSERT 0 2 postgres=# SELECT * FROM temperature; celsius | fahrenheit ---------+------------ 0 | 32 100 | 212 (2 rows)
无法直接将值插入生成列
postgres=# INSERT INTO temperature VALUES (0, 32); ERROR: cannot insert a non-DEFAULT value into column "fahrenheit" DETAIL: Column "fahrenheit" is a generated column.
但是,可以使用 DEFAULT
关键字作为占位符值
postgres=# INSERT INTO temperature VALUES (0, DEFAULT); INSERT 0 1
将 fahrenheit
列转换为普通列(PostgreSQL 13 及更高版本)
postgres=# ALTER TABLE temperature ALTER COLUMN fahrenheit DROP EXPRESSION; ALTER TABLE postgres=# \d temperature Table "public.temperature" Column | Type | Collation | Nullable | Default ------------+----------+-----------+----------+--------- celsius | smallint | | not null | fahrenheit | smallint | | not null |
尝试将非生成列转换为普通列
postgres=# ALTER TABLE temperature ALTER COLUMN celsius DROP EXPRESSION; ERROR: column "celsius" of relation "temperature" is not a stored generated column
上面定义的 temperature
表的 pg_attribute
中条目的部分内容
postgres=# SELECT attname, attnum, attgenerated FROM pg_attribute WHERE attrelid='temperature'::regclass AND attnum > 0; attname | attnum | attgenerated ------------+--------+-------------- celsius | 1 | fahrenheit | 2 | s (2 rows)
生成列表达式存储在 pg_attrdef
中
postgres=# SELECT pg_get_expr(adbin, adrelid, true) FROM pg_attrdef WHERE adrelid = 'temperature'::regclass; pg_get_expr ------------------------ (celsius * 9 / 5 + 32) (1 row)
参考文献
- PostgreSQL 文档: 生成列