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

use*_*258 6 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;
Run Code Online (Sandbox Code Playgroud)

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

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

但是在我执行此之后:

create trigger total2
after update
on totalbooks
for each row
execute procedure total1();
Run Code Online (Sandbox Code Playgroud)

它给我一个错误信息:

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())"
Run Code Online (Sandbox Code Playgroud)

显然我的扳机有问题。请帮忙。

我正在使用Visual C ++ build 1600、64位编译的Postgres 9.2.4

编辑:

我尝试了pg_trigger_depth(),但是现在触发器不会自动更新?我仍然必须执行'select 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();
Run Code Online (Sandbox Code Playgroud)

Sco*_*t S 7

好的,如果您确实想要更新触发器,则可以将其设置为特定于列的触发器,以使触发器不会在更新到时触发all_books,这会导致递归。像这样-

create trigger total2
after update of copy_id
on totalbooks
for each row
execute procedure total1();
Run Code Online (Sandbox Code Playgroud)

当然,您可以更改触发该功能的列,我刚刚选择copy_id它是因为您要计数。

然而

如果要更新count()结果,则只需将触发器置于INSERTDELETE操作上。这样,当计数更改时将触发触发器,但更新本身不会触发该触发器。//编辑:由于您sum仅是中所有记录的计数copies,因此只有在插入或更新记录时它才会更改,因此无论如何在更新时运行此触发器都没有意义。

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

编辑新信息:

考虑到您需要完成的工作,我认为您需要重新考虑数据设计,建议您使用父子关系(每次在表中的许多行上缓存共享数据时,因为它们共享一些共同的东西,表示您可能需要一个父表)。

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

这样,获取份数就和一样简单SELECT COUNT(*) FROM copies WHERE book_id=[some book id]

如果您真的想将计数缓存在某处,请在books表上进行。

为此创建一个INSERT OR UPDATE触发器。copiesUPDATE 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触发器,否则计数中将不考虑新插入/删除的行。