Skip to content

5.3. 生成列#

生成列是一个特殊列,它总是根据其他列计算而来。因此,它对于列来说就像视图对于表一样。生成列有两种类型:存储的和虚拟的。存储的生成列在写入(插入或更新)时计算,并且占用存储空间,就像普通列一样。虚拟的生成列不占用存储空间,并且在读取时计算。因此,虚拟的生成列类似于视图,而存储的生成列类似于物化视图(除了它总是自动更新)。PostgreSQL 目前仅实现存储的生成列。

要创建生成列,请在CREATE TABLE中使用GENERATED ALWAYS AS子句,例如

CREATE TABLE people (
    ...,
    height_cm numeric,
    height_in numeric GENERATED ALWAYS AS (height_cm / 2.54) STORED
);

必须指定关键字STORED以选择存储的生成列类型。有关更多详细信息,请参见CREATE TABLE

无法直接写入生成列。在INSERTUPDATE命令中,无法为生成列指定值,但可以指定关键字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 触发器中访问生成列。