Postgres的。超出了plpgsql堆栈深度限制

时间:2013-05-22 16:15:49

标签: postgresql plpgsql

我正在处理一个简单的函数,它会自动从表中更新某些内容。

create or replace function total() 
returns void as $$
declare
  sum int;
begin
  sum = (SELECT count(copy_id) FROM copies);
    update totalbooks
    set all_books = sum
    where num = 1;
  end;
$$ language plpgsql;

如果我执行"选择total();"它工作得很好所以我做了一个函数触发器,以便它自动更新:

create or replace function total1() returns trigger as $$
begin
   perform (select total());
    return null;
end;
$$ language plpgsql;

但执行此操作后:

create trigger total2
after update
on totalbooks
for each row
execute procedure total1();

它给我一个错误信息:

ERROR:  stack depth limit exceeded
HINT:  Increase the configuration parameter "max_stack_depth" (currently 3072kB), after   ensuring the platform's stack depth limit is adequate.
CONTEXT:  SQL statement "SELECT (SELECT count(copy_id) FROM copies)"
PL/pgSQL function total() line 5 at assignment
SQL statement "SELECT (select total())"
PL/pgSQL function total1() line 3 at PERFORM
SQL statement "update totalbooks
set all_books = sum
where num = 1"
PL/pgSQL function total() line 6 at SQL statement
SQL statement "SELECT (select total())"
PL/pgSQL function total1() line 3 at PERFORM
SQL statement "update totalbooks
set all_books = sum
where num = 1"
PL/pgSQL function total() line 6 at SQL statement
SQL statement "SELECT (select total())"
PL/pgSQL function total1() line 3 at PERFORM
SQL statement "update totalbooks
set all_books = sum
where num = 1"
PL/pgSQL function total() line 6 at SQL statement
SQL statement "SELECT (select total())"
PL/pgSQL function total1() line 3 at PERFORM
SQL statement "update totalbooks
set all_books = sum
where num = 1"
PL/pgSQL function total() line 6 at SQL statement
SQL statement "SELECT (select total())"
PL/pgSQL function total1() line 3 at PERFORM
SQL statement "update totalbooks
set all_books = sum
where num = 1"
PL/pgSQL function total() line 6 at SQL statement
SQL statement "SELECT (select total())"
PL/pgSQL function total1() line 3 at PERFORM
SQL statement "update totalbooks
set all_books = sum
where num = 1"
PL/pgSQL function total() line 6 at SQL statement
SQL statement "SELECT (select total())"

显然我的触发器有问题。请帮忙。

我使用的是Postgres 9.2.4,由Visual C ++ build 1600,64-Bit

编译

编辑:

我试过了pg_trigger_depth(),但是现在触发器没有自动更新?我仍然必须执行'选择total()'

这是我的新代码:

create or replace function total() 
returns void as $$
declare
  sum int;
begin
   sum = (SELECT count(copy_id) FROM copies);
    update totalbooks
    set all_books = sum;
end;
$$ language plpgsql;


create or replace function total1() returns trigger as $$
begin
  perform (select total());
  return null;
end;
$$ language plpgsql;

create trigger total2
after update
on totalbooks
for each row
WHEN (pg_trigger_depth()=0)
execute procedure total1();

3 个答案:

答案 0 :(得分:4)

好的,如果你真的想要更新触发器,你可以做什么把它设置为特定于列的触发器,这样就不会在更新all_books时触发它,这会导致你的递归。像这样的东西 -

create trigger total2
after update of copy_id
on totalbooks
for each row
execute procedure total1();

当然,您可以更改哪些列触发该功能,我只选择了copy_id,因为这是您的计算。

无论其

如果您使用count()结果进行更新,则只需触发INSERTDELETE次操作即可。这样,当计数改变时触发器将触发,但更新不会触发它。 //编辑:由于sum只是copies中所有记录的计数,它只会在插入或更新记录时更改,因此无论如何在更新时运行此触发器都没有意义。 / p>

编辑:我认为添加CREATE TRIGGER Documentation的链接会很有用。请参阅标记为“事件”的部分,因为这详细说明了如何在事件中指定列。

编辑新信息:

鉴于你需要完成的事情,我认为你需要重新思考你的数据设计,我建议你使用父子关系(任何时候你在表中的许多行缓存共享数据,因为它们共享一些东西通常,这是一个标志,你可能需要一个父表)。

有一个books表,其中每一行都是关于一本书(标题,作者等)的信息,然后有一个copies表,其中每一行包含有关一本书的一个副本的信息(序列)号码,最后签出等)。

这样,获得副本的数量就像SELECT COUNT(*) FROM copies WHERE book_id=[some book id]一样简单。

如果你真的想在某处缓存计数,可以在books表上进行。

INSERT OR UPDATE上创建一个copies触发器UPDATE books SET copy_count=(SELECT COUNT(*) FROM copies WHERE book_id=NEW.book_id) WHERE id=NEW.book_id

然后在执行DELETE

的副本上创建UPDATE books SET copy_count=(SELECT COUNT(*) FROM copies WHERE book_id=OLD.book_id) WHERE id=OLD.book_id触发器

两个触发器的原因是NEW变量仅在INSERTUPDATE触发器中可用,而OLD仅在DELETE触发器中可用。你可以把它作为一个触发器来完成,但这需要的代码比我想要的更多。

确保所有触发器都是AFTER触发器,否则计数中不会考虑新插入/删除的行。

答案 1 :(得分:0)

查看pg_trigger_depth:

http://www.postgresql.org/docs/9.2/static/functions-info.html

(9.2或更高版本)

答案 2 :(得分:0)

在触发器内:

IF pg_trigger_depth() < 2 THEN

  PERFORM total();

END IF;