生成列是一个始终从其他列计算得到的特殊列。因此,它对于列来说就像视图对于表一样。有两种类型的生成列:存储的和虚拟的。存储的生成列在写入(插入或更新)时计算,并且占用存储空间,就像它是一个普通列一样。虚拟的生成列不占用存储空间,并且在读取时计算。因此,虚拟的生成列类似于视图,而存储的生成列类似于物化视图(除了它始终自动更新这一点)。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。
不能直接写入生成列。在 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
列列表中指定。