我有一张简单的表格:
CREATE TABLE aaa_has_bbb (
aaa_id integer not null,
bbb_id integer not null,
rank integer not null,
primary key(aaa_id, bbb_id),
uniq(aaa_id, rank)
)
我正在尝试创建一个DELETE和INSERT规则,因为它会激活一些相关的触发器。
CREATE OR REPLACE RULE pivot_key_updates AS
ON UPDATE TO aaa_has_bbb
WHERE OLD.aaa_id<>NEW.aaa_id OR OLD.bbb_id<>NEW.bbb_id
DO INSTEAD (
--
-- on update of keys in this pivot table, delete and insert instead
--
DELETE FROM aaa_has_bbb WHERE aaa_id = OLD.aaa_id
AND bbb_id = OLD.bbb_id;
INSERT INTO aaa_has_bbb (aaa_id, bbb_id, rank)
VALUES (NEW.aaa_id, NEW.bbb_id, NEW.rank);
);
这永远不会插入,但会成功删除。
但是,如果我颠倒这样的顺序:
CREATE OR REPLACE RULE pivot_key_updates AS
ON UPDATE TO aaa_has_bbb
WHERE OLD.aaa_id<>NEW.aaa_id OR OLD.bbb_id<>NEW.bbb_id
DO INSTEAD (
--
-- on update of keys in this pivot table, delete and insert instead
--
INSERT INTO aaa_has_bbb (aaa_id, bbb_id, rank)
VALUES (NEW.aaa_id, NEW.bbb_id, NEW.rank+1);
DELETE FROM aaa_has_bbb WHERE aaa_id = OLD.aaa_id
AND bbb_id = OLD.bbb_id;
);
切换订单有效吗?为什么呢?
为了使这项工作正常,我必须排名+ 1以避免键碰撞,但我并不是真的想要这样做。
我错过了什么?
编辑:我意识到我可以通过触发器让我的生活更轻松,这可能是我最终会做的事情,但我很好奇为什么我的规则不起作用正如所料。
答案 0 :(得分:1)
我测试并复制了你的问题
来自the manual on CREATE RULE
的引用应该揭示这个谜团:
在条件和命令中,特殊表名
NEW
和OLD
可以 用于引用引用表中的值。 NEW在ON中有效INSERT
和ONUPDATE
规则引用要插入的新行或 更新。OLD
在ON UPDATE
和ON DELETE
规则中有效,指代 现有行正在更新或删除。
大胆强调我的
首先DELETE
行时,以下INSERT
无法再找到引用的行,也无法执行任何操作。
我会考虑使用触发器。您可以调整现有的触发器,因此您根本不需要任何其他触发器或规则。
答案 1 :(得分:1)
以下片段适用于简单情况,但第二次(批量)更新失败,可能是由于更新未被where子句限定。
我无法在生成的查询计划中获得正确限定的范围表条目;如果没有where子句,删除目标RTE在最终计划中仍然是不合格的。 (这个
DROP SCHEMA tmp CASCADE;
CREATE SCHEMA tmp ;
SET search_path=tmp;
CREATE TABLE aaa_has_bbb
( aaa_id integer not null
, bbb_id integer not null
, zrank integer not null
, flipflag BOOLEAN NOT NULL DEFAULT True
, primary key(aaa_id, bbb_id)
, unique (aaa_id, zrank)
);
-- I am trying to create a rule which will DELETE and INSERT because that will activate some relevant triggers.
CREATE OR REPLACE RULE pivot_key_updates AS
ON UPDATE TO aaa_has_bbb
WHERE (OLD.aaa_id <> NEW.aaa_id OR OLD.bbb_id <> NEW.bbb_id) AND OLD.flipflag = NEW.flipflag
DO INSTEAD (
--
-- First: copy existing records that fit the criteria
-- The flipflag enables us to distinguish between original and cloned rows
--
INSERT INTO aaa_has_bbb (aaa_id, bbb_id, zrank, flipflag)
SELECT NEW.aaa_id, NEW.bbb_id, NEW.zrank, NOT src.flipflag
FROM aaa_has_bbb src
WHERE src.aaa_id = OLD.aaa_id AND src.bbb_id = OLD.bbb_id
AND src.flipflag = OLD.flipflag
;
-- Next: delete existing records that fit the criteria
DELETE FROM aaa_has_bbb del
WHERE del.aaa_id = OLD.aaa_id AND del.bbb_id = OLD.bbb_id AND del.flipflag = OLD.flipflag
;
);
-- Trigger function to reveal actual operations
CREATE FUNCTION dingdong() RETURNS TRIGGER AS
$func$
BEGIN
RAISE NOTICE 'Table= % operation= % Level= %'
, TG_TABLE_NAME, TG_OP, TG_LEVEL;
RETURN NEW;
END
$func$
LANGUAGE plpgsql;
-- Trigger to reveal actual operations
CREATE TRIGGER aaa_has_bbb_dingdong
AFTER INSERT OR UPDATE OR DELETE ON aaa_has_bbb
FOR EACH ROW EXECUTE PROCEDURE dingdong ();
INSERT INTO aaa_has_bbb(aaa_id, bbb_id, zrank)
VALUES (1,9, 1)
, (2,8, 1)
, (3,7, 1)
, (4,6, 1)
, (5,5, 1)
;
-- This works
-- EXPLAIN ANALYZE
UPDATE aaa_has_bbb up1
SET zrank = 99
, aaa_id = up1.bbb_id
, bbb_id = up1.aaa_id
WHERE up1.aaa_id = 2;
SELECT * FROM aaa_has_bbb;
-- This does not work
-- EXPLAIN ANALYZE
UPDATE aaa_has_bbb up2
SET zrank = 100+up2.zrank
, aaa_id = 100+ up2.aaa_id
WHERE 1=1;
SELECT * FROM aaa_has_bbb;
输出:
DROP SCHEMA
CREATE SCHEMA
SET
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "aaa_has_bbb_pkey" for table "aaa_has_bbb"
NOTICE: CREATE TABLE / UNIQUE will create implicit index "aaa_has_bbb_aaa_id_zrank_key" for table "aaa_has_bbb"
CREATE TABLE
CREATE RULE
CREATE FUNCTION
CREATE TRIGGER
NOTICE: Table= aaa_has_bbb operation= INSERT Level= ROW
NOTICE: Table= aaa_has_bbb operation= INSERT Level= ROW
NOTICE: Table= aaa_has_bbb operation= INSERT Level= ROW
NOTICE: Table= aaa_has_bbb operation= INSERT Level= ROW
NOTICE: Table= aaa_has_bbb operation= INSERT Level= ROW
INSERT 0 5
NOTICE: Table= aaa_has_bbb operation= INSERT Level= ROW
NOTICE: Table= aaa_has_bbb operation= DELETE Level= ROW
UPDATE 0
aaa_id | bbb_id | zrank | flipflag
--------+--------+-------+----------
1 | 9 | 1 | t
3 | 7 | 1 | t
4 | 6 | 1 | t
5 | 5 | 1 | t
8 | 2 | 99 | f
(5 rows)
NOTICE: Table= aaa_has_bbb operation= INSERT Level= ROW
NOTICE: Table= aaa_has_bbb operation= INSERT Level= ROW
NOTICE: Table= aaa_has_bbb operation= INSERT Level= ROW
NOTICE: Table= aaa_has_bbb operation= INSERT Level= ROW
NOTICE: Table= aaa_has_bbb operation= INSERT Level= ROW
NOTICE: Table= aaa_has_bbb operation= DELETE Level= ROW
NOTICE: Table= aaa_has_bbb operation= DELETE Level= ROW
NOTICE: Table= aaa_has_bbb operation= DELETE Level= ROW
NOTICE: Table= aaa_has_bbb operation= DELETE Level= ROW
NOTICE: Table= aaa_has_bbb operation= DELETE Level= ROW
NOTICE: Table= aaa_has_bbb operation= DELETE Level= ROW
NOTICE: Table= aaa_has_bbb operation= DELETE Level= ROW
NOTICE: Table= aaa_has_bbb operation= DELETE Level= ROW
NOTICE: Table= aaa_has_bbb operation= DELETE Level= ROW
NOTICE: Table= aaa_has_bbb operation= DELETE Level= ROW
UPDATE 0
aaa_id | bbb_id | zrank | flipflag
--------+--------+-------+----------
(0 rows)
计划最终更新:= {insert + delete}:
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------
Insert on aaa_has_bbb (cost=0.00..61.19 rows=1 width=13) (actual time=0.082..0.082 rows=0 loops=1)
-> Nested Loop (cost=0.00..61.19 rows=1 width=13) (actual time=0.011..0.032 rows=5 loops=1)
Join Filter: (src.flipflag = up2.flipflag)
-> Seq Scan on aaa_has_bbb up2 (cost=0.00..47.80 rows=9 width=13) (actual time=0.005..0.010 rows=5 loops=1)
Filter: ((flipflag = flipflag) AND ((aaa_id <> (100 + aaa_id)) OR (bbb_id <> bbb_id)))
-> Index Scan using aaa_has_bbb_pkey on aaa_has_bbb src (cost=0.00..1.47 rows=1 width=9) (actual time=0.002..0.002 rows=1 loops=5)
Index Cond: ((aaa_id = up2.aaa_id) AND (bbb_id = up2.bbb_id))
Trigger aaa_has_bbb_dingdong: time=0.293 calls=5
Total runtime: 0.425 ms
Delete on aaa_has_bbb del (cost=0.00..61.19 rows=1 width=12) (actual time=0.075..0.075 rows=0 loops=1)
-> Nested Loop (cost=0.00..61.19 rows=1 width=12) (actual time=0.009..0.047 rows=10 loops=1)
Join Filter: (del.flipflag = up2.flipflag)
-> Seq Scan on aaa_has_bbb up2 (cost=0.00..47.80 rows=9 width=15) (actual time=0.004..0.011 rows=10 loops=1)
Filter: ((flipflag = flipflag) AND ((aaa_id <> (100 + aaa_id)) OR (bbb_id <> bbb_id)))
-> Index Scan using aaa_has_bbb_pkey on aaa_has_bbb del (cost=0.00..1.47 rows=1 width=15) (actual time=0.002..0.002 rows=1 loops=10)
Index Cond: ((aaa_id = up2.aaa_id) AND (bbb_id = up2.bbb_id))
Trigger aaa_has_bbb_dingdong: time=0.494 calls=10
Total runtime: 0.625 ms
Update on aaa_has_bbb up2 (cost=0.00..57.21 rows=1881 width=19) (actual time=0.003..0.003 rows=0 loops=1)
-> Seq Scan on aaa_has_bbb up2 (cost=0.00..57.21 rows=1881 width=19) (actual time=0.002..0.002 rows=0 loops=1)
Filter: ((((aaa_id <> (100 + aaa_id)) OR (bbb_id <> bbb_id)) AND (flipflag = flipflag)) IS NOT TRUE)
Total runtime: 0.023 ms
(24 rows)
答案 2 :(得分:0)
请查看http://www.postgresql.org/docs/9.2/static/rules-update.html,尤其是以“我们最终得到两个最终查询树”为例的示例。
在我看来,规则与触发器的不同之处在于,原始WHERE语句的某些部分会附加到您编写的语句中。 rules-triggers.html中的示例似乎也是这样说的。
如果您想使用单行执行操作,则可以使用“触发器”: http://www.postgresql.org/docs/9.2/static/sql-createtrigger.html