为啥在 PostgreSQL 中创建生成的列时出现错误?
Posted
技术标签:
【中文标题】为啥在 PostgreSQL 中创建生成的列时出现错误?【英文标题】:Why am I getting a an error when creating a generated column in PostgreSQL?为什么在 PostgreSQL 中创建生成的列时出现错误? 【发布时间】:2020-05-31 14:46:08 【问题描述】:CREATE TABLE my_app.person
(
person_id smallserial NOT NULL,
first_name character varying(50),
last_name character varying(50),
full_name character varying(100) generated always as (concat(first_name, ' ', last_name)) STORED,
birth_date date,
created_timestamp timestamp default current_timestamp,
PRIMARY KEY (person_id)
);
错误:生成表达式不是不可变的
目标是将名字和姓氏填充到全名列中。
【问题讨论】:
【参考方案1】:concat()
不是IMMUTABLE
(仅STABLE
),因为它可以调用依赖于区域设置的数据类型输出函数(如timestamptz_out
)。 Tom Lane (core developer) explains it here.
并且first_name || ' ' || last_name
不等同于concat(first_name, ' ', last_name)
,而至少一列可以是NULL
。
详细解释:
Combine two columns and add into one new column解决方案
为了让它发挥作用,完全按照你演示的方式:
CREATE TABLE person (
person_id smallserial PRIMARY KEY
, first_name varchar(50)
, last_name varchar(50)
, full_name varchar(101) GENERATED ALWAYS AS
(CASE WHEN first_name IS NULL THEN last_name
WHEN last_name IS NULL THEN first_name
ELSE first_name || ' ' || last_name END) STORED
, ...
);
db小提琴here
CASE
表达式的速度非常快 - 比多个连接和函数调用快得多。而且完全正确。
或者,如果你知道你在做什么并且拥有必要的权限,创建一个IMMUTABLE
concat-function,如下所示(替换CASE
表达式):
除此之外:full_name
必须是 varchar(101)
(50+50+1) 才有意义。或者只使用 text
列。见:
一般建议
最佳解决方案取决于您计划如何准确处理 NULL 值(和空字符串)。我可能不会添加生成的列,这通常比动态连接全名更昂贵且更容易出错。考虑一个观点。或者一个封装了确切串联逻辑的函数。
相关:
Computed / calculated / virtual / derived columns in PostgreSQL Store common query as column?【讨论】:
【参考方案2】:这适用于||
运算符:
CREATE TABLE person (
person_id smallserial NOT NULL,
first_name character varying(50),
last_name character varying(50),
full_name character varying(100) generated always as (first_name || ' ' || last_name) STORED,
birth_date date,
created_timestamp timestamp default current_timestamp,
PRIMARY KEY (person_id)
);
我不确定concat()
被视为可变的技术原因,但||
不是。
如果你想处理列中的NULL
值,那就有点复杂了。我可能会推荐:
trim(both ' ' from
(' ' || coalesce(first_name, '') || ' ' || coalesce(last_name, '')
)
)
当然,这与您的表达式完全不一样,因为它会删除名称开头和结尾的空格。
【讨论】:
是的,这没有意义。谢谢! 替换不等价。以上是关于为啥在 PostgreSQL 中创建生成的列时出现错误?的主要内容,如果未能解决你的问题,请参考以下文章
使用 Python 在 Flask 中创建端点时出现问题 [重复]