pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
生成列是一种特殊列,其值总是根据其他列计算得出。因此,对列而言,它就像视图对表一样。生成列有两种:存储型和虚拟型。存储型生成列在写入时(插入或更新时)计算,并像普通列一样占用存储空间。虚拟生成列不占用存储空间,而是在读取时计算。因此,虚拟生成列类似于视图,存储型生成列类似于物化视图(只是它总会自动更新)。PostgreSQL 目前只实现了存储型生成列。
要创建生成列,可使用 GENERATED ALWAYS AS 子句,在 CREATE TABLE 中,例如:
CREATE TABLE people (
...,
height_cm numeric,
height_in numeric GENERATED ALWAYS AS (height_cm / 2.54) STORED
);
必须指定关键字 STORED 来选择存储型生成列。参见 CREATE TABLE 了解更多细节。
生成列不能直接写入。在 INSERT 或 UPDATE 命令中,不能为生成列指定值,但可以指定关键字 DEFAULT。
带默认值的列和生成列之间存在一些差异。如果没有提供其他值,列默认值只在行首次插入时计算一次;生成列则会在行每次变化时更新,并且不能被覆盖。列默认值不能引用同一表中的其他列,而生成表达式通常会这样做。列默认值可以使用易失性函数,例如 random() 或引用当前时间的函数;生成列则不允许这样做。
生成列以及包含生成列的表在定义时有若干限制:
生成表达式只能使用不可变函数,并且不能使用子查询,也不能以任何方式引用当前行之外的任何内容。
生成表达式不能引用另一个生成列。
生成表达式不能引用系统列,tableoid 除外。
生成列不能具有列默认值或标识定义。
生成列不能是分区键的一部分。
外部表可以有生成列。详情见 CREATE FOREIGN TABLE。
对于继承和分区:
如果父列是生成列,其子列也必须是生成列;不过,子列可以使用不同的生成表达式。在插入或更新一行时,实际应用的是该行所在物理表关联的生成表达式。(这与列默认值不同:默认值使用的是查询中所指名表关联的默认值。)
如果父列不是生成列,其子列也不能是生成列。
对于继承表,如果在 CREATE TABLE ... INHERITS 中编写子列定义时未写任何 GENERATED 子句,则会自动从父列复制该 GENERATED 子句。ALTER TABLE ... INHERIT 要求父列和子列在是否为生成列这一点上已经匹配,但不要求它们的生成表达式相同。
类似地,对于分区表,如果在 CREATE TABLE ... PARTITION OF 中编写子列定义时未写任何 GENERATED 子句,则会自动从父列复制该 GENERATED 子句。ALTER TABLE ... ATTACH PARTITION 要求父列和子列在生成状态上已经匹配,但不要求它们的生成表达式相同。
在多重继承的情况下,如果某个父列是生成列,则所有父列都必须是生成列。如果它们的生成表达式并不完全相同,则必须为子列显式指定所需的表达式。
生成列的使用还有以下注意事项。
生成列与其底层基础列分别维护访问权限。因此,可以安排成某个角色能够读取生成列,但不能读取底层基础列。
从概念上说,生成列会在 BEFORE 触发器运行之后更新。因此,BEFORE 触发器中对基础列所做的修改会反映到生成列中。反过来,在 BEFORE 触发器中访问生成列是不允许的。
逻辑复制会跳过生成列,并且不能在 CREATE PUBLICATION 的列列表中指定生成列。
译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。英文原文本身的问题请通过上游文档表单反馈给 PostgreSQL 文档维护者。