在 Postgresql 中创建生成列时出错

Error when creating a generated column in Postgresql

CREATE TABLE Person (
   id  serial primary key,
   accNum  text UNIQUE GENERATED ALWAYS AS (
 concat(right(cast(extract year from current_date) as text), 2), cast(id as text)) STORED
);

错误:生成表达式不是不可变的

目标是用 YYid 填充 accNum 字段,其中 YY 是添加此人的年份的最后两个字母。

我也试过'||'操作员但未成功。

我不确定如何解决这个问题(可能是触发器),但 current_date 是一个稳定的函数,而不是一个不可变的函数。对于生成的 ID,我相信所有函数调用都必须是不可变的。您可以在此处阅读更多内容 https://www.postgresql.org/docs/current/xfunc-volatility.html

我不认为任何获取日期的函数都是不可变的,因为 Postgres 将其定义为“一个 IMMUTABLE 函数不能修改数据库,并且保证 return 在给定相同参数的情况下永远得到相同的结果。”对于 return 当前日期的任何内容,这都不成立。

我认为最好的办法是使用触发器来执行此操作,以便在插入时设置值。

正如@ScottNeville 正确提到的那样:

CURRENT_DATE 不是不可变的。所以它不能用于 GENERATED ALWAYS AS 表达式。

但是,您仍然可以使用触发器实现此目的:

demo:db<>fiddle

CREATE FUNCTION accnum_trigger_function() 
   RETURNS TRIGGER 
   LANGUAGE PLPGSQL
AS $$
BEGIN
   NEW.accNum := right(extract(year from current_date)::text, 2) || NEW.id::text;
   
   RETURN NEW;
END
$$;

CREATE TRIGGER tr_accnum
  BEFORE INSERT
  ON person
  FOR EACH ROW
  EXECUTE PROCEDURE accnum_trigger_function();

正如评论中正确提到的@a_horse_with_no_name:您可以将表达式简化为:

NEW.accNum := to_char(current_date, 'YY') || NEW.id;

由于您不希望更新列,所以当行发生变化时,您可以定义自己的函数来生成数字:

create function generate_acc_num(id int)
returns text
as
$$
  select to_char(current_date, 'YY')||id::text;
$$
language sql
immutable; --<< this is lying to Postgres!

请注意,您应该永远不要将此功能用于任何其他目的。 特别是不作为索引表达式。

然后您可以在生成的列中使用它:

CREATE TABLE Person 
(
  id  integer generated always as identity primary key,
  acc_num  text UNIQUE GENERATED ALWAYS AS (generate_acc_num(id)) STORED
);