【问题标题】:Trigger seems to be fired after transaction, not after command/statement触发器似乎在事务之后被触发,而不是在命令/语句之后
【发布时间】:2014-12-02 23:21:45
【问题描述】:

我有两个表foobar,还有一个after triggerfoo 上,它改变了bar 表上的一些东西。

create table foo(id serial primary key, _key char(128) not null);
create table bar(id bigserial primary key, _key_p char(256) not null);

所以当我有一个事务时,例如调用下面的函数,触发器在事务之后触发,而不是语句或相关的 dml 操作。
foo 表的触发器:

create or replace function foo_trg_func()returns trigger as $$
declare k_p char(256);begin
k_p:=(select res from prepare_pa(new.key));
insert into bar(_key_p) values(k_p);--insert it to the bar
end $$ language plpgsql;
--
create trigger foo_trg
after insert on foo
for each row execute procedure foo_trg_func();

示例函数/事务

create or replace function `bas`(int,character(128))returns int as $$
-- some commands
with res as (select res as "d" from c_key($1,$2)),
-- attemp to insert into foo and expect the insertion to bar too
ins as (insert into foo(_key) select d from res returning 1)  --line[5]
-- check the effect of the foo_trg
select _key_p from bar,res where _key_p=res.d;    --line[7]

$$ language sql

触发器被调用,数据被foo触发器插入到bar表中,但是在函数调用之后,我无法在第7行得到触发器插入的结果。 我现在该怎么做?

我还要提一下,可以将触发器标记为代替或之前,但是会引起很多变化,所以我想知道是否可以使用after触发器。 p>

【问题讨论】:

  • 不应该是:ins as (insert into foo(_key) select d from res returning id)?
  • 是的,先生,错字,谢谢。 @丹尼斯
  • @Denis:添加的RETURNING 子句没有任何作用,CTE ins 没有被引用。编辑是一个误解,与问题正交。
  • 那么你有答案了吗?
  • @ErwinBrandstetter 还没有先生,如果我想忘记触发器并按照您提到的那样做事情,我必须编码几个月并再次重新编码。目前我在调用函数后选择结果,但想知道为什么用这么完美的数据库不可能!?

标签: sql postgresql triggers transactions common-table-expression


【解决方案1】:

为什么?

首先,您必须知道您正在运行一个单个语句(带有多个 CTE。)

单个查询的常用表表达式参见数据库的同一张快照。每个 CTE(以及最终命令)都可以重用之前 CTE(内部临时表)的输出,但对基础表的影响不可见。您的最终 SELECT 看不到 CTE ins 对基础表的任何影响。

之后一个单独的SELECT(即使在同一个事务中,也可以在同一个函数中)会看到效果。

见:

适合您的情况的解决方案

您有两个相互冲突的要求:

  1. 您希望使用基础表中的更改行(CTE 的 RETURNING 子句不可用,因为这些是另一个表上的触发器的副作用)。但变化在同一语句中不可见

  2. 您希望将其与带有 RETURNING 子句的 CTE 返回的值结合起来,但这些在带有 CTE 的语句之外不可见

通过在TEMPORARY TABLE 中具体化来自 CTE 的派生表来解决此问题:

CREATE OR REPLACE FUNCTION bas (int, text)
  RETURNS SETOF int
  LANGUAGE plpgsql AS
$func$
BEGIN
   CREATE TEMP TABLE foo_tmp (LIKE foo) ON COMMIT DROP;

   WITH ins AS (
      INSERT INTO foo(_key)
      SELECT res FROM c_key($1,$2) f
      RETURNING foo.*
      )
   INSERT INTO foo_tmp
   SELECT * FROM ins;

   RETURN QUERY
   SELECT b._key_p
   FROM   bar b
   JOIN   foo_tmp f ON b._key_p = f._key;
END
$func$;

或者,您可以移除触发器并手动执行INSERT(如果这是单点入口)。

旁白

CREATE OR REPLACE FUNCTION foo_trg_func()
  RETURNS trigger
  LANGUAGE plpgsql AS
$func$
BEGIN
   INSERT INTO bar(_key_p)
   SELECT res FROM prepare_pa(NEW.key);
END
$func$;

如果prepare_pa() 返回单个值,则更简单。

【讨论】:

  • 我认为他的效果更像是:其中一个 CTE 没有产生任何行,所以最后一条语句没有返回任何内容。
  • @Denis:我不这么认为。其实我觉得有误会。我会在上面发表评论。
猜你喜欢
  • 1970-01-01
  • 2013-11-23
  • 2022-06-22
  • 1970-01-01
  • 2016-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-05
相关资源
最近更新 更多