为何新增生成列的ALTER TABLE报错‘生成表达式非不可变’?
解决PostgreSQL存储生成列报错“ERROR: generation expression is not immutable”
问题根源在current_timestamp函数——它属于不稳定(volatile)函数,每次调用返回的结果都会随时间变化。而存储生成列(STORED)要求表达式必须是**不可变(immutable)**的,因为存储列的值会在数据插入/更新时计算并固定存储,不能依赖动态变化的值。
你的需求是实时判断账号是否有效(尤其是过期时间的判断),更适合用虚拟生成列(VIRTUAL),它会在每次查询时实时计算,不需要表达式满足immutable要求。以下是简化并修正后的代码:
ALTER TABLE IF EXISTS vnext.users ADD COLUMN is_valid boolean GENERATED ALWAYS AS ( trim(coalesce(email, '')) <> '' AND trim(coalesce(legacy_password, '')) <> '' AND (expiry_date IS NULL OR date_trunc('day', expiry_date) > date_trunc('day', current_timestamp)) ) VIRTUAL;
补充说明:
- 简化了原语句的嵌套CASE逻辑,功能完全等价,但可读性更强
- 虚拟列不会占用额外存储空间,每次查询时实时计算,能准确反映当前时间下的账号有效性状态
如果一定要使用存储列(注意:存储列的is_valid值不会随时间自动更新,除非手动触发数据更新),可以通过触发器实现:
- 先添加普通列:
ALTER TABLE IF EXISTS vnext.users ADD COLUMN is_valid boolean;
- 创建触发器函数:
CREATE OR REPLACE FUNCTION update_is_valid() RETURNS TRIGGER AS $$ BEGIN NEW.is_valid := trim(coalesce(NEW.email, '')) <> '' AND trim(coalesce(NEW.legacy_password, '')) <> '' AND (NEW.expiry_date IS NULL OR date_trunc('day', NEW.expiry_date) > date_trunc('day', current_timestamp)); RETURN NEW; END; $$ LANGUAGE plpgsql;
- 创建触发器:
CREATE TRIGGER trigger_update_is_valid BEFORE INSERT OR UPDATE ON vnext.users FOR EACH ROW EXECUTE FUNCTION update_is_valid();
内容的提问来源于stack exchange,提问作者LordDraagon
相关产品推荐
相关产品推荐

