受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发版本: devel

5.4. 生成列 #

生成列是一种特殊列,其值总是根据其他列计算得出。因此,对列而言,它就像视图对表一样。生成列有两种:存储型和虚拟型。存储型生成列在写入时(插入或更新时)计算,并像普通列一样占用存储空间。虚拟生成列不占用存储空间,而是在读取时计算。因此,虚拟生成列类似于视图,存储型生成列类似于物化视图(只是它总会自动更新)。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子句,则会自动从父列复制该子句。ALTER TABLE ... ATTACH PARTITION要求父列和子列在生成状态上已经匹配,但不要求它们的生成表达式相同。

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

生成列的使用还有以下注意事项。

  • 生成列与其底层基础列分别维护访问权限。因此,可以安排成某个角色能够读取生成列,但不能读取底层基础列。

    对于虚拟生成列,只有在生成表达式只使用防泄漏函数时,这种安排才完全安全(见CREATE FUNCTION),但系统不会强制检查这一点。

  • 生成表达式中所用函数的权限,会在表达式实际执行时分别在写入或读取时检查,就像生成表达式是由使用生成列的查询直接调用的一样。生成列的用户必须拥有调用生成表达式中所有函数的权限。生成表达式中的函数是以执行查询的用户的权限还是函数所有者的权限来执行,取决于这些函数被定义为SECURITY INVOKER还是SECURITY DEFINER

  • 从概念上说,生成列会在BEFORE触发器运行之后更新。因此,BEFORE触发器中对基础列所做的修改会反映到生成列中。反过来,在BEFORE触发器中访问生成列是不允许的。

  • 生成列会在逻辑复制中被跳过,并且不能出现在CREATE PUBLICATION的列列表中。

提交更正

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