Как написать правило удаления в представлении? - PullRequest
5 голосов
/ 04 апреля 2011

Я пытаюсь написать правило для представления для удаления кортежей из таблиц компонентов, но пока могу удалить данные только из одной из них.Некоторое время я использовал postgres с базовыми представлениями, но у меня нет опыта работы с правилами для представлений.

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

Таблицы компонентов:

CREATE TABLE parent(
   id serial PRIMARY KEY,
   p_data integer NOT NULL UNIQUE
);
CREATE TABLE child(
   id serial PRIMARY KEY,
   parent_id integer NOT NULL UNIQUE REFERENCES parent(id),
   c_data integer NOT NULL
);

Представление:

CREATE TABLE child_view(
   id integer,
   p_data integer,
   c_data integer
);
CREATE RULE "_RETURN" AS ON SELECT TO child_view DO INSTEAD
   SELECT child.id, p_data, c_data 
      FROM parent JOIN child ON (parent_id=parent.id);

Правило удаления проблемы

CREATE RULE child_delete AS ON DELETE TO child_view DO INSTEAD(
   DELETE FROM child WHERE id=OLD.id;
   DELETE FROM parent WHERE p_data=OLD.p_data;
);

Цель этого правила состоит в том, чтобы удалить кортежи, на которые имеются ссылки в представлении, изТаблицы компонентов.WHERE p_data=OLD.p_data мне кажется странным, но я не вижу, как еще ссылаться на нужный кортеж в родительской таблице.

Вот что происходит, когда я пытаюсь использовать указанное выше правило:

>SELECT * FROM child_view;
 id | p_data | c_data 
----+--------+--------
  1 |      1 |     10
  2 |      2 |     11
  3 |      3 |     12
(3 rows)

>DELETE FROM child_view WHERE id=3;
DELETE 0

>SELECT * FROM child_view;
 id | p_data | c_data 
----+--------+--------
  1 |      1 |     10
  2 |      2 |     11
(2 rows)

Но, глядя на родительскую таблицу, вторая часть удаления не работает (id = 3 "должен" был удален):

>SELECT * FROM parent;
 id | p_data 
----+--------
  1 |      1
  2 |      2
  3 |      3
(3 rows)

Как мне написать правило удаления вудалить дочерний и родительский кортежи?

Это использует postgres v9.

Любая помощь приветствуется.Также приветствуются ссылки на любые материалы, касающиеся правил представлений за пределами документации postgres (если я явно не пропустил что-то).Спасибо.

РЕДАКТИРОВАТЬ: как указывает jmz, было бы проще использовать каскадное удаление, чем правило здесь, но этот подход не работает для моей реальной схемы.

Ответы [ 2 ]

5 голосов
/ 04 апреля 2011

Проблема с правилом заключается в том, что система правил не обрабатывает данные атомарно.Первое удаление выполняется независимо от порядка двух операторов в правиле DO INSTEAD.Второй оператор никогда не выполняется, потому что строка, на которую ссылается OLD.id, была удалена из представления.Вы можете использовать LEFT JOIN, но это не поможет вам из-за структуры примера таблицы (она может работать на вашей фактической схеме базы данных).

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

Лучше всего использовать внешние ключи и ON DELETE CASCADE вместо правил.С ними будет работать и ваша примерная схема: вам нужно будет только удалить родительскую таблицу, чтобы избавиться от всех дочерних элементов.

0 голосов
/ 05 апреля 2011

То, что вы хотите сделать, будет работать нормально. Но вы сделали левый поворот на это:

CREATE TABLE child_view(
   id integer,
   p_data integer,
   c_data integer
);
CREATE RULE "_RETURN" AS ON SELECT TO child_view DO INSTEAD
   SELECT child.id, p_data, c_data 
      FROM parent JOIN child ON (parent_id=parent.id);

Тебе нужен реальный вид здесь, а не стол. Вот почему удаление не будет работать.

CREATE VIEW child_view AS SELECT
    child.id,
    p_data,
    c_data
    FROM parent
        JOIN child ON (parent_id=parent.id)
;

Замените верхнюю часть на нижнюю, и она будет отлично работать (это было, когда я тестировал ее). Причина, по которой удаление не работает, - это попытка удалить идентификатор из дочернего представления TABLE, которое, конечно, пусто! Он не выполняет правило «выберите вместо», поэтому он работает с дочерним представлением реальной таблицы. Люди могут какашкать по правилам, но если они не видят такой очевидной ошибки, мне интересно, сколько они знают?

Я успешно использовал правила при определении интерфейсов для обеспечения соблюдения бизнес-правил. Они могут приводить элегантные решения так, как не могли бы спусковые механизмы.

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

Редактировать: скрипт на запрос

-- set this as you may have had an error if you running 
-- from a script and not noticed it with all the NOTICES
\set ON_ERROR_STOP

drop table if exists parent cascade;
drop table if exists child cascade;

CREATE TABLE parent(
        id serial PRIMARY KEY,
        p_data integer NOT NULL UNIQUE
);

CREATE TABLE child(
        id serial PRIMARY KEY,
        parent_id integer NOT NULL UNIQUE REFERENCES parent(id),
        c_data integer NOT NULL
);

        CREATE VIEW child_view AS SELECT
                child.id,
                p_data,
                c_data
                FROM parent 
                        JOIN child ON (parent_id=parent.id)
        ;

CREATE RULE child_delete AS ON DELETE TO child_view DO INSTEAD(
        DELETE FROM child WHERE id=OLD.id;
        DELETE FROM parent WHERE p_data=OLD.p_data;
);

insert into parent (p_data) values (1), (2), (3);
insert into child (parent_id, c_data) values (1, 1), (2, 2), (3, 3);

select * from child_view;

 id | p_data | c_data 
----+--------+--------
  1 |      1 |      1
  2 |      2 |      2
  3 |      3 |      3
(3 rows)

delete from child_view where id=3;
DELETE 0

select * from child_view;
 id | p_data | c_data 
----+--------+--------
  1 |      1 |      1
  2 |      2 |      2
(2 rows)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...