在 postgresql 中更新语句后没有触发规则

Posted

技术标签:

【中文标题】在 postgresql 中更新语句后没有触发规则【英文标题】:Rule not fired after update statement in postgresql 【发布时间】:2011-03-30 14:36:48 【问题描述】:

按照www.postgresql.org中的指南进行操作

我创建了这个声明:

CREATE TABLE shoelace_log (
sl_name    text,          -- shoelace changed
sl_avail   integer,       -- new available value
log_who    text,          -- who did it
log_when   timestamp      -- when);
CREATE RULE log_shoelace AS ON UPDATE TO shoelace_data
WHERE NEW.sl_avail <> OLD.sl_avail
DO INSERT INTO shoelace_log VALUES (
                                NEW.sl_name,
                                NEW.sl_avail,
                                current_user,
                                current_timestamp
                            );

当我尝试在 pgAdmin3 查询控制台中执行查询时:

UPDATE shoelace_data SET sl_avail = 6 WHERE sl_name = 'sl7';

只执行更新查询,不触发规则。我尝试使用命令 EXPLAIN 来了解原因。

EXPLAIN UPDATE shoelace_log SET sl_avail = 6 WHERE sl_name = 'sl7';

日志说:

"Seq Scan on shoelace_log  (cost=0.00..19.66 rows=4 width=32)"
"  Filter: (sl_name = 'sl7'::text)"
"Seq Scan on shoelace_log  (cost=0.00..19.62 rows=4 width=78)"
"  Filter: (sl_name = 'sl7'::text)"

我也尝试在 EXPLAIN 中使用 VERBOSE 选项,但我不明白为什么我的事件没有被触发。

【问题讨论】:

我尝试在 pg9 上运行您链接到的示例并遇到同样的问题。 您说您的 pgAdmin3 查询是更新 shoelace_data,但您解释更新 shoelace_log(日志表)...您确定您是输入您认为正在输入的查询? 【参考方案1】:

您选择了错误的功能。完全忘记规则——它们非常困难,几乎不可能做到正确。

你需要的是一个触发器:

create or replace function shoelace_log_who_when()
returns trigger language plpgsql as
$$
begin
  if OLD.sl_avail is distinct from NEW.sl_avail then
    NEW.log_who = current_user;
    NEW.log_when = current_timestamp;
  end if;
  return NEW;
end;
$$;

create trigger shoelace_log_who_when before update on shoelace_log
  for each row execute procedure shoelace_log_who_when();

试试看:

插入 shoelace_log 值('foo', 1, NULL, NULL);
从鞋带日志中选择 *;
sl_name | sl_avail |日志谁 | log_when
---------+---------+---------+----------
富 | 1 | |
更新 shoelace_log 设置 sl_avail=2;
从鞋带日志中选择 *;
 sl_name | sl_avail |日志谁 | log_when
---------+---------+----------+------ ---------
 富 | 2 |托梅茨基 | 2011-03-30 17:06:21.07137

【讨论】:

+1 很好的答案,但仍然没有解释为什么这样一个简单的例子不起作用。 这就是为什么你不应该使用规则。它们非常复杂,即使是经验丰富的 Postgres 用户也不会试图解释他们的行为。我认为他们被考虑抛弃 Postgres —在 9.1 中的视图上使用 INSTEAD OF 触发器,它们不再需要任何东西。 太糟糕了,pg 文档没有说明你不应该使用它们。我已经阅读了一些为什么它们很痛苦并且应该避免的原因,但并不是因为它们不像文档描述的那样起作用。 OP 混合了 datalog 表 - 当前者以某种方式更新时,他想插入后者。您将需要一个不同的触发程序。 (重新阅读问题,在 EXPLAIN 混淆之前停止......)【参考方案2】:

您可能还想考虑使用为您执行此操作的预构建插件:

http://pgfoundry.org/projects/tablelog/

【讨论】:

不是tablelog。这只是为了在表中填写自动上次修改的时间戳和用户。【参考方案3】:

我尝试了这个示例,它确实有效。但我必须先创建 shoelace_data 表。该表不在示例中。

create table shoelace_data (
    sl_name text,
    sl_avail integer
);
insert into shoelace_data values ('Catcall', 3);
update shoelace_data set sl_avail = 5;

现在,如果您从 log 表中进行选择。 . .

select * from shoelace_log;
"Catcall";5;"postgres";"2011-03-31 20:40:10.906"

请注意,当 shoelace_data 中的 sl_avail 列有更新时,该规则要求插入 shoelace_log

【讨论】:

以上是关于在 postgresql 中更新语句后没有触发规则的主要内容,如果未能解决你的问题,请参考以下文章

如何为没有已知列的 PostgreSQL 视图编写通用更新触发器?

MySQL触发器的使用规则

postgreSQL创建一个触发器函数:更新过student1表的数据后,更新student1_stats表中数据。

插入后触发,更新相关模型Postgresql

我想在触发器中计算时间码块

postgreSQL触发器