PostgreSQL 17: 更改生成列表达式的 ALTER TABLE 命令

John Doe 五月 19, 2025

当你在表中使用生成列时,有想过要更改生成列的表达式吗?现在,PostgreSQL 已经支持更改生成列表达式了。

在山坡漫步的大象

特性提交日志

支持更改生成列表达式的 ALTER TABLE 命令。

该补丁添加了一个新的 ALTER TABLE 子命令ALTER COLUMN ... SET EXPRESSION,用于修改生成列的生成表达式。

虽然该语法并非标准 SQL,但其借鉴了其他 SQL 实现的语法风格。

此命令会使用 PostgreSQL 常规的 ALTER TABLE 机制,触发表重建。其实现方式与SET DATA TYPE子命令的底层架构类似,并复用了部分基础逻辑(例如,重建约束和索引)。新命令需要在AlterTablePass中新增一个处理步骤,同时ADD COLUMN步骤需往前调整,以确保ADD COLUMNSET EXPRESSION的组合操作能够正常运行。

讨论https://www.postgresql.org/message-id/flat/CAAJ_b94yyJeGA-5M951_Lr+KfZokOp-2kXicpmEhi5FXhBeTog@mail.gmail.com

示例

能够更改生成列表达式是非常实用的。尽管这需要表重建和长时间锁表,但是这是必要的。

我们通过示例来看具体用法:

CREATE TABLE testing (  
    id int8 GENERATED ALWAYS AS IDENTITY PRIMARY KEY,  
    when_ts TIMESTAMP,  
    when_year int4 GENERATED ALWAYS AS (EXTRACT(YEAR FROM when_ts)::int4) STORED  
);  

INSERT INTO testing (when_ts)  
SELECT NOW() - RANDOM() * '10 years'::INTERVAL FROM GENERATE_SERIES(1,5) i;

SELECT * FROM testing;
 id |          when_ts           | when_year
----+----------------------------+-----------
  1 | 2019-05-29 03:21:02.964805 |      2019
  2 | 2018-03-25 16:52:43.707205 |      2018
  3 | 2022-04-22 17:57:53.652805 |      2022
  4 | 2015-03-14 21:11:22.875205 |      2015
  5 | 2020-02-10 11:27:18.171205 |      2020
(5 rows)  

现在,我们可以修改生成表达式:

ALTER TABLE testing ALTER COLUMN when_year SET EXPRESSION AS (EXTRACT(YEAR FROM when_ts)::int4 - 2000);

SELECT * FROM testing;
 id |          when_ts           | when_year
----+----------------------------+-----------
  1 | 2019-05-29 03:21:02.964805 |        19
  2 | 2018-03-25 16:52:43.707205 |        18
  3 | 2022-04-22 17:57:53.652805 |        22
  4 | 2015-03-14 21:11:22.875205 |        15
  5 | 2020-02-10 11:27:18.171205 |        20
(5 rows)

显然,这个功能非常实用。

不过,我们需要注意 SQL 语法的混淆使用。让我们来看下相关的语法:

psql -c '\h alter table' | grep -i expression
    ALTER [ COLUMN ] column_name [ SET DATA ] TYPE data_type [ COLLATE collation ] [ USING expression ]
    ALTER [ COLUMN ] column_name SET DEFAULT expression
    ALTER [ COLUMN ] column_name SET EXPRESSION AS ( expression )
    ALTER [ COLUMN ] column_name DROP EXPRESSION [ IF EXISTS ]
  CHECK ( expression ) [ NO INHERIT ] | { CHECK ( expression ) [ NO INHERIT ] | { column_name | ( expression ) } [ opclass ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ]

可以看到,现有的更改列的语法中,包括了SET DATA TYPE x USING expressionSET DEFAULT expressionSET EXPRESSION AS ()。SQL 语法通常较为冗长,这里或许可以多添加一个词,例如SET GENERATED EXPRESSION AS,使语义更清晰。

非常不错的特性,感谢所有参与的社区人员。

参考

提交日志:https://git.postgresql.org/pg/commitdiff/5d06e99a3cfc23bbc217b4d78b8c070ad52f720e