PostgreSQL中的生成列:虚拟与存储

在现代数据库系统中,如SQL Server和Oracle,计算列(Computed Columns)是一个常见的特性。它们允许数据库表中某些列的值基于其他列动态计算得出。然而,直到PostgreSQL12版本之前,PostgreSQL并不原生支持计算列,这使得从其他数据库迁移到PostgreSQL变得相对困难。本文旨在探讨在不同版本的PostgreSQL中实现类似计算列功能的多种方法。

PostgreSQL12是这个世界上最受欢迎且功能丰富的开源数据库的下一个主要版本。计划于2019年底发布,它支持生成列。在PostgreSQL中,使用GENERATED ALWAYS AS子句来创建生成列。用于定义生成列的表达式被称为生成表达式。

在PostgreSQL中,创建带有生成列的表的语法如下:

CREATE TABLE employee ( ..., dob timestamp, age integer GENERATED ALWAYS AS ( (date_part('year', CURRENT_TIME) - date_part('year', dob)) STORED );

与SQL Server的语法相比,SQL Server并没有特殊的关键字来声明计算列。只需要在AS子句后面指定计算列的表达式。

CREATE TABLE Employee ( ..., dob datetime, age AS DATEDIFF(year, dob, GETDATE()) PERSISTED );

注意PERSISTED子句,它等同于PostgreSQLSTORED子句。SQL Server还支持非持久化计算列,只需要不指定PERSISTED子句即可。但PostgreSQL目前仅实现了存储生成列。

当行首次插入且未提供其他值时,列默认值仅评估一次;生成列则在行发生变化时更新,且不能被覆盖。带有DEFAULT约束的列可以在INSERTUPDATE语句中赋予值,而生成列则不能赋予值,它们总是被计算得出。

带有DEFAULT约束的列不能引用表中的其他列,而生成列正是为了实现这一点。带有DEFAULT约束的列可以使用易变函数,例如random()current_time,而生成列则不能。

限制和限制条件

定义生成列和包含生成列的表时,有一些限制条件:

  • 生成表达式只能使用不可变函数,不能使用易变函数。
  • 生成表达式不能使用子查询或以任何方式引用当前行以外的任何内容。
  • 生成表达式不能引用另一个生成列。
  • 生成表达式不能引用系统列,除了tableoid
  • 生成列不能有列默认值或身份定义。
  • 生成列不能是分区键的一部分。

额外考虑

外键表可以有生成列。生成列的访问权限与其底层基础列是分开维护的。因此,可以授予角色对生成列的读取权限,但不是对底层基础列的权限。

从概念上讲,生成列在BEFORE触发器运行后更新。因此,在BEFORE触发器中对基础列所做的更改将反映在生成列中。但生成列本身不能在BEFORE触发器中被访问。

截至本文撰写之时,PostgreSQL 12的稳定版本尚未发布,运行在旧版本的工作负载可能仍然需要此功能。在PostgreSQL 11.x及更早版本中,有两种方法可以实现:

在这种方法中,表没有'age'列。需要'age'列时使用视图。

CREATE VIEW v_employee AS SELECT dob, date_part('year', CURRENT_TIME) - date_part('year', dob) as age FROM employee;

在这种方法中,'age'被声明为常规整数列,触发器在插入和更新时填充该列。

CREATE TABLE employee ( ..., dob timestamp, age integer ); CREATE OR REPLACE FUNCTION calc_age() RETURNS TRIGGER AS $body$ BEGIN NEW.age := date_part('year', CURRENT_TIME) - date_part('year', dob); RETURN NEW; END; $body$ LANGUAGE plpgsql;
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485