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

Ror*_*ory 8 database postgresql triggers type-conversion database-trigger

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

我正在尝试BEFORE INSERT在表上写一个触发器,它将在数据上调用此函数,以便我可以确保没有插入类型错误.然而,它似乎不起作用,我在尝试插入数据时遇到错误,即使触发器在那里.

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

我希望这个在postgresql 9.3上运行,但我注意到postgres 9.5上的错误和无效触发器.

Pat*_*ick 8

不,你不能使用这种方法.原因是后端已经使用要插入表中的值填充记录.这是NEW触发器中可用参数的形式.因此,即使在触发器触发之前,也会抛出错误.

顺便提一下,这同样适用于规则,因此凯文在他的评论中的建议是行不通的.

可能你最好的解决方案是创建一个具有"许可"列数据类型(例如text)的临时表,然后BEFORE INSERT在该表上放置一个触发器,在将所有列值插入最终表之前将其转换为正确的类型.如果第二次插入成功,你甚至可以RETURN NULL从插入,所以行不会进入表格(不确定,但是,COPY考虑的是......).最终在表中的那些记录中包含一些奇怪的数据,然后您可以手动处理这些行.


Gar*_*ary 7

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

没有第二个表的另一种方法是在基表上创建一个视图,将所有内容转换为varchar,然后INSTEAD OF在视图上尝试插入时,使用一个触发器填充基表.

例如,下面的表格tab1有一个整数列.视图v_tab1具有varchar,因此任何插入都将适用于视图.而不是触发器然后检查输入的值是否为数字,如果不是则使用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();
Run Code Online (Sandbox Code Playgroud)

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

insert into v_tab1 values ('12','hello');
insert into v_tab1 values ('banana','world');
select * from tab1;
Run Code Online (Sandbox Code Playgroud)

给予

|i1   |v1   |
+-----+-----+
|12   |hello|
|0    |world|
Run Code Online (Sandbox Code Playgroud)

小提琴:http://sqlfiddle.com/#!15/ 9af5ab/1