0

我有一个简单的表:

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)
)

我正在尝试创建一个将删除和插入的规则,因为这将激活一些相关的触发器。

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 以避免密钥冲突,但我真的不想这样做。

我错过了什么?

编辑:我意识到我可以使用触发器让我的生活更轻松,这可能就是我最终要做的,但我很好奇为什么我的规则不能按预期工作。

4

3 回答 3

1

我测试并重现了您的问题。手册中的
这句话应该可以解释这个谜团:CREATE RULE

在条件和命令中,特殊的表名NEWOLD可用于引用被引用表中的值。NEW 在 ON INSERT和 ONUPDATE规则中有效,以引用正在插入或更新的新行。在和规则OLD中有效,以引用正在更新或删除的现有行ON UPDATEON DELETE

大胆强调我的。
当您DELETE先行时,以下内容INSERT无法再找到引用的行并且什么也不做。

我会考虑改用触发器。您可能能够调整现有触发器,因此您根本不需要任何额外的触发器或规则

于 2013-06-05T01:50:05.737 回答
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)

计划最终更新:= {插入+删除}:

                                                    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)
于 2013-06-05T13:33:55.450 回答
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

于 2013-06-05T01:27:55.370 回答