2017-08-04 58 views
0

基本上我有table_a和table_b。 table_b由来自table_a的特征构成,并且通常它们共享section_id列和状态。当另一个表被插入或更新时,Postgresql触发器更新表中的列

表-A的SECTION_ID是主键,所以这是唯一的,但表-B可以有多个section_ids,但他们都有着同样的地位

用户插入和更新表-B,我想捕捉状态更改回表-A

CREATE TRIGGER table_b_aiu 
AFTER INSERT OR UPDATE 
ON table_b 
FOR EACH ROW 
WHEN (((new.status = 100) OR (new.status = 200))) 
EXECUTE PROCEDURE table_b_aiu(); 

CREATE OR REPLACE FUNCTION table_b_aiu() 
RETURNS trigger AS 
$BODY$ 
BEGIN 
UPDATE table_a a 
SET status = 100 
FROM table_b b 
WHERE (b.status = 100 or b.status = 200) 
AND a.section_id = b.section_id; 
    RETURN new; 
END; 
$BODY$ 
LANGUAGE plpgsql VOLATILE 
COST 100; 

问题在于,它不是只更新最新更新或插入的行,而是更新整个数据集,这并非意图。如何仅更新新行?我试图把争论的TRIGER,但它不接受new.section_id

+0

是'section_id'独具一格。行的标识符?对th的定义有帮助还有问题的表格。 – cole

+0

@ C.Arendt section_id是table_a的主键,但不是table_b的 – Luffydude

+1

您是什么意思的触发器参数?像这样:'EXECUTE PROCEDURE table_b_aiu(new.section_id);'? 您有权访问该行而不将其作为参数传递。您可以简单地执行'where a.section_id = new.section_id'并从'table_b'中移除'因为它不是必需的。 –

回答

1

所以有几件事。对于这种类型的问题,访问测试表格定义肯定有帮助。我嘲笑了我期望的桌子设计。此外,包含“所需行为”(即,是否打算始终将状态设置为100?无论table_b发生什么情况)都很有帮助。此外,update声明必然会遇到整个table_a,这就是为什么您需要唯一约束才能获得唯一记录。如果您错误地将状态设置为100,那么它应该是SET status = new.status,这将是一个不同的问题(但可能看起来更新是“击中整个表”,请参阅下面的示例 - 特别是section_id=3)。

希望以下说明准确的行为。我怀疑你的触发器应该设置status=new.status

CREATE TABLE table_a 
(
section_id serial 
, status integer 
, CONSTRAINT pk_table_a PRIMARY KEY (section_id) 
); 

CREATE TABLE table_b 
(
id serial 
, section_id integer 
, status integer 
, CONSTRAINT pk_table_b_aiu PRIMARY KEY (id) 
) 
; 


CREATE OR REPLACE FUNCTION table_b_aiu() 
RETURNS trigger AS 
$BODY$ 
BEGIN 
UPDATE table_a a 
SET status = 100 -- intentional?? 
WHERE (new.status = 100 or new.status = 200) 
AND a.section_id = new.section_id; 

    RETURN new; 
END; 
$BODY$ 
LANGUAGE plpgsql VOLATILE 
COST 100; 

CREATE TRIGGER table_b_aiu 
AFTER INSERT OR UPDATE 
ON table_b 
FOR EACH ROW 
WHEN (((new.status = 100) OR (new.status = 200))) 
EXECUTE PROCEDURE table_b_aiu(); 


INSERT INTO table_a (section_id, status) 
values (1,100) 
, (2,200) 
, (3,201) 
, (4, 202) 
returning *; 

| section_id | status | 
| 1 | 100 | 
| 2 | 200 | 
| 3 | 201 | 
| 4 | 202 | 

INSERT INTO table_b (section_id, status) 
values (1,101), (2,100), (3,200), (4,201) 
returning *; 

| id | section_id | status | 
| 1 | 1 | 101 | 
| 2 | 2 | 100 | 
| 3 | 3 | 200 | 
| 4 | 4 | 201 | 

select * 
from table_a; 

| section_id | status | 
| 1 | 100 | 
| 4 | 202 | 
| 2 | 100 | 
| 3 | 100 | 

注:new.status in (100,200)是多余的,但我想你想成为安全(万一有人曾是建立一个触发没有when声明

我的建议:如果你想对表本身的状态选项限制二百分之百,我建议建立一个外键的status表,只有具有这些选项

+0

好的,所以预期的行为是在table_a上留下状态100,无论如何。唯一的一个场合,为什么我想将状态从100更改为其他内容,如果table_b上的行无效并被删除,但我想这是另一个触发器 – Luffydude

+1

您肯定可以将该逻辑放在同一个触发器中。总的来说,我认为这样会更好,但如果可能以避免触发的方式进行关系设计,那么这可能还是可取的。上述工作是否符合您的期望? (如果你需要测试,你可以'SET search_path = otherschema;'来避免你已经有定义表的模式)。 – cole

+1

还有一个想法 - 您将用户插入日志表并更新父级。可以让用户插入或更新父表并使用触发器创建日志表(这是我通常遵循的方法)。见倒数第二个例子[这里](https://www.postgresql.org/docs/9.4/static/sql-createtrigger.html) – cole

1

试试这个功能(虽然没有测试)

CREATE OR REPLACE FUNCTION table_b_aiu() 
RETURNS trigger AS 
$BODY$ 
BEGIN 
    UPDATE table_a 
    SET status = 100 
    WHERE section_id = NEW.section_id 
    RETURN new; 
END; 
$BODY$ 

直到section_idtable_a主键应该是足够了。

相关问题