一个生成列
始终根据其他列计算的特殊列。
生成列在 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 18
- 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 文档: 生成列