生成列

始终根据其他列计算得出的特殊列

一个生成列 是一个始终根据其他列计算得出的特殊列。

生成列是在PostgreSQL 12中引入的。

功能

截至PostgreSQL 12,生成列只能为 STORED 类型。提交 fc22b662 指示另一种类型的生成列 VIRTUAL 可能会在将来某个日期添加。

PostgreSQL 13 开始,可以使用 ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION 命令将生成列转换为普通列。

PostgreSQL 17 开始,可以更改用于创建生成列的表达式。

实现

attgenerated 列已添加到 pg_attribute 中,以指示该列是否为生成列;s 表示该列为 STORED 类型,否则该值为为空,并且该列不是生成列。

外部数据包装器 (FDW) 支持

外部表 可以使用生成列定义,并且生成的值将提供给底层的 外部数据包装器。FDW 应确保将值存储在外部服务器上,并且可以随后检索,但是不能保证检索到的值不会在外部服务器上更改。

更改历史记录

示例

带生成列的表的简单示例,此处用于预先计算过时的温度转换

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)

分类

DDL存储

反馈

提交任何关于 "生成列" 的评论、建议或更正 此处