生成的列

一个特殊列,它总是从其他列计算得来

生成列 是一个特殊列,它总是从其他列计算得来。

生成列在 PostgreSQL 12 中引入。

功能

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

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

PostgreSQL 18 开始,生成列也可以是 VIRTUAL 类型。请注意,这将成为默认的虚拟列类型。

实现

已将列 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, 存储

反馈

在此处提交有关“生成列”的任何评论、建议或更正