下面的片段适用于简单的情况,但第二次(批量)更新失败,可能是由于更新没有被 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)
计划最终更新:= {插入+删除}:
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)