2025年9月25日: PostgreSQL 18 发布!
支持版本: 当前 (18) / 17 / 16 / 15 / 14 / 13
开发版本: devel
不支持版本: 12

5.4. 生成列 #

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

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

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

生成列默认是虚拟类型。使用 VIRTUALSTORED 关键字可以明确选择。有关更多详细信息,请参阅 CREATE TABLE

生成列不能直接写入。在 INSERTUPDATE 命令中,不能为生成列指定值,但可以指定 DEFAULT 关键字。

请注意列的默认值和生成列之间的区别。列的默认值在插入行时仅评估一次(如果未提供其他值);生成列在行发生更改时更新,并且不能被覆盖。列的默认值不能引用表中的其他列;而生成表达式通常会这样做。列的默认值可以使用易变函数(volatile functions),例如 random() 或引用当前时间的函数;这不允许用于生成列。

生成列以及包含生成列的表的定义有几个限制:

  • 生成表达式只能使用不可变函数(immutable functions),不能使用子查询,也不能以任何方式引用当前行之外的内容。

  • 生成表达式不能引用另一个生成列。

  • 生成表达式不能引用系统列,但 tableoid 除外。

  • 虚拟生成列不能具有用户定义的类型,并且虚拟生成列的生成表达式不能引用用户定义的函数或类型,也就是说,它只能使用内置函数或类型。这也间接适用,例如对于函数或类型,它们是运算符或强制转换的基础。(存储型生成列不存在此限制。)

  • 生成列不能具有列默认值或身份定义。

  • 生成列不能作为分区键的一部分。

  • 外表可以拥有生成列。有关详细信息,请参阅 CREATE FOREIGN TABLE

  • 关于继承和分区

    • 如果父列是生成列,则其子列也必须是相同类型(存储型或虚拟型)的生成列;但是,子列可以具有不同的生成表达式。

      对于存储型生成列,在插入或更新行时实际应用的生成表达式是与该行物理存储的表相关联的那个。(这与列默认值的行为不同:对于列默认值,应用查询中命名的表的默认值。)对于虚拟生成列,在读取表时应用查询中命名的表的生成表达式。

    • 如果父列不是生成列,则其子列也不能是生成的。

    • 对于继承表,如果在 CREATE TABLE ... INHERITS 中定义子列时不带任何 GENERATED 子句,则其 GENERATED 子句将自动从父表中复制。 ALTER TABLE ... INHERIT 会强制要求父子列在生成状态上已匹配,但不会要求它们的生成表达式也匹配。

    • 对于分区表,如果在 CREATE TABLE ... PARTITION OF 中定义子列时不带任何 GENERATED 子句,则其 GENERATED 子句将自动从父表中复制。 ALTER TABLE ... ATTACH PARTITION 会强制要求父子列在生成状态上已匹配,但不会要求它们的生成表达式也匹配。

    • 在多重继承的情况下,如果一个父列是生成列,则所有父列都必须是生成列。如果它们不具有相同的生成表达式,则必须显式指定子列所需的表达式。

生成列的使用还有其他注意事项。

  • 生成列独立于其底层基数列维护访问权限。因此,可以安排某个角色可以读取生成列,但不能读取其底层基数列。

    对于虚拟生成列,只有当生成表达式仅使用 leakproof 函数(参见 CREATE FUNCTION)时,这才是完全安全的,但这不会被系统强制执行。

  • 在生成表达式中使用的函数的权限在表达式实际执行时(分别在写入或读取时)进行检查,就好像生成表达式直接从使用生成列的查询中调用一样。生成列的用户必须具有调用生成表达式中所有函数的权限。生成表达式中的函数将根据函数是否定义为 SECURITY INVOKERSECURITY DEFINER,以执行查询的用户或函数所有者的权限来执行。

  • 生成列的概念上是在 BEFORE 触发器运行之后更新的。因此,在 BEFORE 触发器中对基列所做的更改将反映在生成列中。但反之,不允许在 BEFORE 触发器中访问生成列。

  • 根据 CREATE PUBLICATION 参数 publish_generated_columns 或通过将它们包含在 CREATE PUBLICATION 命令的列列表中,允许在逻辑复制期间复制生成列。目前这仅支持存储型生成列。有关详细信息,请参阅 第 29.6 节

提交更正

如果您在文档中发现任何不正确、与您对特定功能的使用体验不符或需要进一步说明的内容,请使用 此表单 报告文档问题。