2013-06-05 2 views
0

у меня есть простая таблица:Postgres правило обновления обыкновение вставлять

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, чтобы избежать столкновения ключей, но я действительно не хочу этого делать.

Что мне не хватает?

EDIT: Я понимаю, что могу облегчить свою жизнь с помощью триггеров, и это, вероятно, то, что я в конечном итоге сделаю, но мне очень любопытно, почему мое правило не работает должным образом.

+0

PostgreSQL версия? Кроме того, определение таблицы не соответствует определению правила 'rank' vs.' ccc'? –

+0

Извините, Эрвин, мой анонимайзер ушел от меня, я исправил все ссылки на ccc -> rank –

+0

Вы * еще * не предоставили свою версию Postgres. –

ответ

1

Я тестировал и воспроизведен вашу проблему.
Эта цитата из the manual on CREATE RULE должна пролить свет на тайну:

В состоянии и команды, специальные имена таблиц NEW и OLD могут быть использованы для обозначения значений в указанной таблице. NEW действует в ON INSERT и ON UPDATE правила для ссылки на новую вставленную строку или . OLD действителен в ON UPDATE и ON DELETE правил до ссылается на существующая строка обновляется или удаляется.

Смелый акцент мой.
Когда вы начинаете DELETE, строка INSERT больше не может найти ссылку на строку и не делает ничего.

Вместо этого я бы предпочла использовать триггеры. Возможно, вы сможете настроить существующие триггеры, поэтому вам не нужны никакие дополнительные триггеры или правила вообще.

+0

Вы утверждаете, что внутри моего правила значение NEW и OLD изменено запросами, которые я запускаю? не будет ли мой delete генерировать его собственный NEW и OLD внутри его собственной сферы? у него есть свои триггеры после ... –

+1

@DavidChan: Правила работы полностью отличаются от триггеров. В функции триггера нет переменных. 'NEW' и' OLD' относятся к фактической строке в таблице. Как только вы УДАЛИТЬ это, 'NEW' и' OLD' пусты. Я думаю, что цитата из руководства делает это довольно ясным. –

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

+0

Да, он также упоминает, что условия применяются только к отдельным правилам, и я использую многократное правило ... к сожалению, нет примера. Также синтаксис в документах, по-видимому, подразумевает, что мне нужно обернуть мое правило multi в фигурные скобки {(rule ..; rule;)}, но это не сработает. –

1

Нижеприведенный фрагмент работает для простого случая, но не выполняется для второго (пакетного) обновления, вероятно, вызванного тем, что обновление не было присвоено предложением where-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) 
Смежные вопросы