在 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 混合了 data 和 log 表 - 当前者以某种方式更新时,他想插入后者。您将需要一个不同的触发程序。 (重新阅读问题,在 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 视图编写通用更新触发器?