让我们使用测试表:
CREATE TABLE labs.date_test
(
pkey int NOT NULL,
val integer,
date timestamp without time zone,
CONSTRAINT date_test_pkey PRIMARY KEY (pkey)
);
我有一个如下定义的触发器功能。它是一个将日期插入表中指定列的函数。它的参数是主键,日期字段的名称和要插入的日期:
CREATE OR REPLACE FUNCTION tf_set_date()
RETURNS trigger AS
$BODY$
DECLARE
table_name text;
pkey_col text := TG_ARGV[0];
date_col text := TG_ARGV[1];
date_val text := TG_ARGV[2];
BEGIN
table_name := format('%I.%I', TG_TABLE_SCHEMA, TG_TABLE_NAME);
IF TG_NARGS != 3 THEN
RAISE 'Wrong number of args for tf_set_date()'
USING HINT='Check triggers for table ' || table_name;
END IF;
EXECUTE format('UPDATE %s SET %I = %s' ||
' WHERE %I = ($1::text::%s).%I',
table_name, date_col, date_val,
pkey_col, table_name, pkey_col )
USING NEW;
RAISE NOTICE '%', NEW;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
实际的触发器定义如下:
CREATE TRIGGER t_set_ready_date
BEFORE UPDATE OF val
ON labs.date_test
FOR EACH ROW
EXECUTE PROCEDURE tf_set_date('pkey', 'date', 'localtimestamp(0)');
现在说我做:INSERT INTO TABLE
date_test(pkey)values(1);`
然后我按如下方式执行更新:
UPDATE labs.date_test SET val = 1 WHERE pkey = 1;
现在按预期插入日期。但val
字段仍为NULL
。它没有人们期望的1
(或者更像我期望的那样)。
我做错了什么?触发器中的RAISE NOTICE显示NEW仍然是我所期望的。 UPDATE
触发器中是否允许BEFORE UPDATE
?一个comment约postgres triggers似乎表明,如果BEFORE UPDATE触发器中存在UPDATE语句,则原始UPDATE会被覆盖。有人可以帮助我吗?
我正在尝试更新调用触发器的同一个表,以及调用触发器的UPDATE语句要修改的同一行。我正在运行Postgresql 9.2
答案 0 :(得分:1)
我不确定,但是你的触发器可以执行递归调用 - 它会从UPDATE触发器更新相同的表。这通常是不好的做法,并且编写过于通用的触发器通常不是一个好主意。但我不知道你在做什么,也许你需要它,但你必须确定,所以你可以防止递归。
对于触发器的调试,可以很好地启动和结束函数体调试消息。可能在EXECUTE语句之后使用GET DIAGNOSTICS语句获取有关动态SQL影响的信息
DECLARE
_updated_rows int;
_query text;
BEGIN
RAISE NOTICE 'Start trigger function xxx';
...
_query := format('UPDATE ....);
RAISE NOTICE 'dynamic sql %, %', _query, new;
EXECUTE _query USING new;
GET DIAGNOSICS _updated_rows = ROW_COUNT;
RAISE NOTICE 'Updated rows %', _updated_rows;
...
答案 1 :(得分:1)
鉴于所有动态表名称,如果此触发器在调用触发器的同一个表上发出update
,则不完全清楚。
如果是这样:那不行。您UPDATE some_table
上的BEFORE
触发器中无法some_table
。或者,更严格地说,你可以,但是如果你更新受语句影响的任何行,那么调用触发器的结果将是不可预测的,因此通常不是一个好主意。
相反,直接更改NEW
中的值。遗憾的是,您无法使用动态列名称执行此操作;您只需自定义触发器或使用AFTER
触发器在行已更改后执行更新。