我created_at在 postgres 表中有一个日期列。我有一个长时间运行的查询,按周对数据进行分组,因此查询的一部分是
(SELECT TO_CHAR(score.created_at, 'IYYY-IW') AS week,
customer_id, AVG(score) AS avg_week_score FROM score
Run Code Online (Sandbox Code Playgroud)
这是低效的,我想使用 Postgres 12 中新生成的列功能来存储每个插入日期的文本版本。
我试着跑
alter table score add column gen_week text generated always as (TO_CHAR(created_at, 'IYYY-IW')) stored;
Run Code Online (Sandbox Code Playgroud)
但得到
ERROR: generation expression is not immutable
Run Code Online (Sandbox Code Playgroud)
我猜这与 created_at 依赖于语言环境,尤其是时区这一事实有关,所以我尝试了
alter table score add column week_gen text generated always as (TO_CHAR(created_at::timestamptz at time zone 'UTC', 'IYYY-IW')) stored;
Run Code Online (Sandbox Code Playgroud)
使时区明确,但这会产生相同的错误。
有没有办法使 to_char 与日期对象保持不变,或者我是否必须基于 to_char 定义我自己的不可变函数?
问题是它to_char() 可能会从语言环境中进行修改——并且函数要么是不可变的,要么不是不可变的。
您可以使用以下方法执行相同的操作extract():
alter table score add column gen_week text generated always as
((extract(isoyear from created_at) * 100 + extract(week from created_at))::text ) stored;
Run Code Online (Sandbox Code Playgroud)
实际上,上面没有添加连字符,所以:
alter table score add column gen_week text generated always as
(extract(isoyear from created_at)::text || '-' || lpad(extract(week from created_at)::text, 2, '0')) stored;
Run Code Online (Sandbox Code Playgroud)
这是一个 db<>fiddle。