生成列是一种特殊的列,它始终从其他列计算得出。因此,它对列的作用类似于视图对表的作用。生成列有两种类型:存储型和虚拟型。存储型生成列在写入(插入或更新)时计算,并占用存储空间,就像普通列一样。虚拟型生成列不占用存储空间,并在读取时计算。因此,虚拟型生成列类似于视图,而存储型生成列类似于物化视图(不同之处在于它始终自动更新)。
要创建生成列,请在 CREATE TABLE 中使用 GENERATED ALWAYS
AS 子句,例如:
CREATE TABLE people (
...,
height_cm numeric,
height_in numeric GENERATED ALWAYS AS (height_cm / 2.54)
);
生成列默认是虚拟型。使用关键字 VIRTUAL 或 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 将坚持要求父列和子列在生成状态上匹配,但不要求它们的生成表达式匹配。
在多重继承的情况下,如果一个父列是生成列,则所有父列必须是生成列。如果它们的生成表达式不完全相同,则必须明确指定子列所需的表达式。
使用生成列时需要考虑额外的因素。
生成列与其基础列的访问权限是分开维护的。因此,可以安排特定角色可以从生成列读取,但不能从基础列读取。
对于虚拟生成列,只有在生成表达式仅使用防泄漏函数时(见 CREATE FUNCTION)才是完全安全的,但系统并不强制执行这一点。
在实际执行生成表达式时,会检查生成表达式中使用的函数的权限,分别在写入或读取时,就像生成表达式是直接从使用生成列的查询中调用的一样。生成列的用户必须拥有调用生成表达式中所有函数的权限。生成表达式中的函数以执行查询的用户或函数拥有者的权限执行,具体取决于函数是否定义为 SECURITY INVOKER 或 SECURITY DEFINER。
从概念上讲,生成列在 BEFORE 触发器运行后更新。因此,在 BEFORE 触发器中对基础列所做的更改将反映在生成列中。但相反,在 BEFORE 触发器中不允许访问生成列。
在逻辑复制过程中,生成列可以根据 CREATE PUBLICATION 参数
publish_generated_columns 进行复制,或通过将它们包含在 CREATE PUBLICATION 命令的列列表中。这目前仅支持存储生成列。有关详细信息,请参见 第 29.6 节。