我正在尝试创建一个触发器,这样当我添加新记录时,它会在同一个表中添加另一条记录。会话字段只会取1到4之间的值。因此,当我在会话中添加1时,我希望它添加另一条记录,但会话3被阻止。但问题是它会导致级联触发器并且它会一次又一次地插入,因为插入时会触发触发器。
我有一个简单的表格:
CREATE TABLE example
(
id SERIAL PRIMARY KEY
,name VARCHAR(100) NOT NULL
,session INTEGER
,status VARCHAR(100)
);
我的触发功能是:
CREATE OR REPLACE FUNCTION add_block() RETURNS TRIGGER AS $$
BEGIN
INSERT INTO example VALUES (NEW.id + 1, NEW.name, NEW.session+2, 'blocked');
RETURN NULL;
END;
$$ LANGUAGE 'plpgsql';
触发器是:
CREATE TRIGGER add_block
AFTER INSERT OR UPDATE
ON example
FOR EACH ROW
EXECUTE PROCEDURE add_block();
我收到错误:
SQL statement "INSERT INTO example VALUES ( $1 +1, $2 , $3 + 2, $4)"
PL/pgSQL function "add_block" line 37 at SQL statement
这个错误重复了很多次,以至于我无法看到顶部。
我该如何解决这个问题?
编辑:
CREATE TABLE block_rules
(
id SERIAL PRIMARY KEY
,session INTEGER
,block_session INTEGER
);
此表包含块规则。因此,如果使用会话1将新记录插入到EXAMPLE表中,则会通过在上面的相同(EXAMPLE)表中插入具有阻塞状态的新记录(而不是block_rules)来相应地阻止会话3。对于会话2也是如此,但它会阻止会话4。
block_rules表包含阻止会话的规则(或模式)。它持有
id | session | block_session
------------------------------
1 | 1 | 3
2 | 2 | 4
3 | 3 | 2
我如何把这个触发器的WHEN声明放在下面的Erwin Branstetter的答案中?
由于
答案 0 :(得分:1)
好的,所以你不能只添加另一个列,如下所示:
ALTER TABLE example ADD COLUMN trig INTEGER DEFAULT 0;
CREATE OR REPLACE FUNCTION add_block() RETURNS TRIGGER AS $$
BEGIN
IF NEW.trig = 0 THEN
INSERT INTO example VALUES (NEXTVAL('example_id_seq'::regclass), NEW.name, NEW.session+2, 'blocked', 1);
END IF;
RETURN NULL;
END;
$$ LANGUAGE 'plpgsql';
它不是很好,但它有效: - )
答案 1 :(得分:1)
此触发器功能根据表block_rules
中的信息添加被阻止的会话。
我假设表格由 id
链接 - 问题中缺少信息。
我现在假设块规则是所有会话的一般规则,并且session
链接。仅针对非阻塞会话调用触发器并插入匹配的阻止会话。
触发功能:
CREATE OR REPLACE FUNCTION add_block()
RETURNS TRIGGER AS
$BODY$
BEGIN
INSERT INTO example (name, session, status)
VALUES (NEW.name
,(SELECT block_session
FROM block_rules
WHERE session = NEW.session)
,'blocked');
RETURN NULL;
END;
$BODY$ LANGUAGE plpgsql;
触发:
CREATE TRIGGER add_block
AFTER INSERT -- OR UPDATE
ON example
FOR EACH ROW
WHEN (NEW.status IS DISTINCT FROM 'blocked')
EXECUTE PROCEDURE add_block();
仍有改进的余地。考虑这个设置:
CREATE OR REPLACE FUNCTION add_block()
RETURNS TRIGGER AS
$BODY$
BEGIN
INSERT INTO example (name, session, status)
VALUES (NEW.name, NEW.session + 2, 'blocked');
RETURN NULL;
END;
$BODY$ LANGUAGE plpgsql;
CREATE TRIGGER add_block
AFTER INSERT -- OR UPDATE
ON example
FOR EACH ROW
WHEN (NEW.session < 3)
-- WHEN (status IS DISTINCT FROM 'blocked') -- alternative guess at filter
EXECUTE PROCEDURE add_block();
对于PostgreSQL 9.0或更高版本,您可以使用WHEN condition in the trigger definition。这将是最有效的。对于旧版本,您在触发器功能中使用相同的条件。
如果您可以定义标准以识别自动插入的行,则无需添加列。你没告诉,所以我假设在我的例子中只有自动插入的行有session > 2
。我为WHEN
添加了另一个status = 'blocked'
条件作为评论。
您应该始终为INSERT提供列列表。如果不这样做,以后对表格的更改可能会产生意想不到的副作用!
手动不在触发器中插入NEW.id + 1
。这不会递增序列,而下一个INSERT
会因重复的密钥违规而失败。
id
是serial
列,所以不要做任何事情。序列中的默认nextval()
会自动插入。
您的说明仅提及INSERT
,但您有一个触发器AFTER INSERT OR UPDATE
。我删掉了UPDATE
部分。
不必引用关键字plpgsql
。
答案 2 :(得分:0)
CREATE OR REPLACE FUNCTION add_block() RETURNS TRIGGER AS $$
BEGIN
SET SESSION session_replication_role = replica;
INSERT INTO example VALUES (NEXTVAL('example_id_seq'::regclass), NEW.name, NEW.session+2, 'blocked');
SET SESSION session_replication_role = origin;
RETURN NULL;
END;
$$ LANGUAGE 'plpgsql';