postgres更新规则不会插入

时间:2013-06-05 00:12:49

标签: sql postgresql triggers

我有一张简单的表格:

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以避免键碰撞,但我并不是真的想要这样做。

我错过了什么?

编辑:我意识到我可以通过触发器让我的生活更轻松,这可能是我最终会做的事情,但我很好奇为什么我的规则不起作用正如所料。

3 个答案:

答案 0 :(得分:1)

我测试并复制了你的问题 来自the manual on CREATE RULE的引用应该揭示这个谜团:

  

在条件和命令中,特殊表名NEWOLD可以   用于引用引用表中的值。 NEW在ON中有效   INSERT和ON UPDATE规则引用要插入的新行或   更新。 OLDON UPDATEON 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