在 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
表达式。
但是,您仍然可以使用触发器实现此目的:
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
);
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
表达式。
但是,您仍然可以使用触发器实现此目的:
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
);