使用“BEFORE INSERT”触发器更改传入数据的数据类型以匹配 PostgreSQL 中的列数据类型

Using `BEFORE INSERT` trigger to change the datatype of incoming data to match the column datatype in PostgreSQL

我有一个 postgres table,其列 C 的类型为 T。人们将使用 COPY 将数据插入此 table。但是有时他们会尝试为 C 插入一个不是 T 类型的值,但是我有一个可以将值转换为 T 的 postgres 函数。

我正在尝试在 table 上编写一个 BEFORE INSERT 触发器,它会在数据上调用此函数,这样我就可以确保不会出现插入类型错误。但是它似乎不起作用,我在尝试插入数据时遇到错误,即使那里有触发器也是如此。

在我花太多时间调查之前,我想看看这是否可行。我可以通过这种方式使用触发器来更改传入数据的类型吗?

我想在 postgresql 9.3 上 运行,但我注意到 postgres 9.5 上的错误和非功能触发器。

不,你不能使用这种方法。原因是后端已经用要插入 table 的值填充了一条记录。这是触发器中可用的 NEW 参数的形式。因此,甚至在触发器触发之前就会抛出错误。

顺便说一句,这同样适用于规则,所以凯文在他的评论中的建议是行不通的。

可能您最好的解决方案是创建一个具有 "permissive" 列数据类型(例如 text)的暂存 table,然后在该 BEFORE INSERT 上放置一个触发器 table 将所有列值转换为正确的类型,然后再将它们插入最终的 table。如果第二次插入成功,您甚至可以从插入中 RETURN NULL,这样该行就不会进入 table(不过,不确定 COPY 对此有何看法...) .最终出现在 table 中的那些记录中有一些奇怪的数据,然后您可以手动处理这些行。

正如帕特里克所说,您必须指定一个允许的目标,以便 Postgres 验证不会在您有机会操纵数据之前拒绝数据。

另一种没有第二个 table 的方法是在您的基础 table 上创建一个视图,将所有内容都转换为 varchar,然后有一个 INSTEAD OF 触发器来填充基础 table 每当在视图上尝试插入时。

例如,下面的 table tab1 有一个整数列。视图 v_tab1 改为使用 varchar,因此任何插入都适用于该视图。 instead of 触发器然后检查输入的值是否为数字,如果不是,则使用 0。

create table tab1 (i1 int, v1 varchar);

create view v_tab1 as select cast(i1 as varchar) i1, v1 from tab1;

create or replace function v_tab1_insert_trgfun() returns trigger as
$$
declare
  safe_i1 int;
begin
  if new.i1 ~ '^([0-9]+)$' then
     safe_i1 = new.i1::int;
  else
     safe_i1 = 0;
  end if;

  insert into tab1 (i1, v1) values (safe_i1, new.v1);
  return new;
end;
$$
language plpgsql;

create trigger v_tab1_insert_trigger instead of insert on v_tab1  for each row execute procedure v_tab1_insert_trgfun();

现在无论值如何,插入都可以工作

insert into v_tab1 values ('12','hello');
insert into v_tab1 values ('banana','world');
select * from tab1;

给予

|i1   |v1   |
+-----+-----+
|12   |hello|
|0    |world|

Fiddle 在:http://sqlfiddle.com/#!15/9af5ab/1