2009-08-24 3 views
10

Когда я использую правило перезаписи, которое разбивает вставку на одну таблицу на вставки в две другие таблицы, где одно из вставленных значений имеет значение по умолчанию nextval ('some_sequence ') с одинаковой последовательностью для обеих таблиц, то введенные значения по умолчанию различаются в двух таблицах. Вероятно, это связано с простой заменой текста правилом перезаписи. Вместо этого я надеялся, что значение по умолчанию будет сначала разрешено, а затем одно и то же значение будет записано в обе таблицы.Правила PostgreSQL и проблема nextval()/serial (очень специфичные для PostgreSQL)

Вот пример (как вы, наверное, догадались, что я пытаюсь реализовать специализации/обобщение с помощью правил):

-- first and third commands can be skipped if id is defined as serial 
create sequence parents_id_seq; 
create table Parents(
    id integer default(nextval('parents_id_seq')) primary key, 
    type varchar(50) not null check(type in ('Child1', 'Child2')), 
    unique (id, type), 
    attribute1 varchar(50) not null unique check(length(attribute1) > 0) 
); 
alter sequence parents_id_seq owned by parents.id; 

Данные, специфичного для детей первого рода хранится в

create table Partial_Children1(
    id integer default(nextval('parents_id_seq')) primary key, 
    type varchar(50) not null check(type = 'Child1'), 
    foreign key (id, type) references Parents(id, type), 
    attribute2 varchar(50) not null check(length(attribute2) > 0) 
); 

Далее я определил представление Children1, которое объединяет обе приведенные выше таблицы (я переписал представление, указав явно, что PostgreSQL делает для определения представлений в соответствии с документацией)

create table Children1(
    id int default(nextval('parents_id_seq')), 
    type varchar(50) not null check(type in ('Child1')), 
    attribute1 varchar(50) not null check(length(attribute1) > 0), 
    attribute2 varchar(50) not null check(length(attribute2) > 0) 
); 
create rule "_RETURN" as on select to Children1 do instead 
    select p.*, c.attribute2 
    from Parents p 
    join Partial_Children1 c 
     on p.id = c.id; 

Наконец правило, переписывают у меня проблемы с:

create rule ct_i_children1 as 
    on insert to Children1 
    do instead (
    insert into Parents(attribute1, type) 
     values(new.attribute1, 'Child1'); 
    insert into Partial_Children1(attribute2, type) 
     values(new.attribute2, 'Child1'); 
); 

Попытка вставить данные с

insert into Children1 (attribute1, attribute2) 
    values ('a1', 'a2'), 
     ('b1', 'b2'); 

дает сообщение об ошибке,

ERROR: insert or update on table "partial_children1" violates foreign key constraint "partial_children1_id_fkey" 
DETAIL: Key (id,type)=(3,Child1) is not present in table "parents". 

способ решить эту заменяет вторую вставку правила перезаписи на

insert into Partial_Children1(id, attribute2, type) 
    select p.id, new.attribute2, p.type 
    from Parents p 
    where p.attribute1 = new.attribute1 

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

У кого-нибудь есть идея, как получить одинаковые значения по умолчанию в обеих таблицах (только с использованием правил и не триггеров)?

ответ

5

Из документов http://www.postgresql.org/docs/8.4/static/rules.html

Она (система правил) модифицирует запросы принять правила во внимание, и затем передает модифицированный запрос к планированию запроса для планирования и исполнения

поэтому он сначала перезаписывает запросы, не выполняя ничего.

вы можете заставить его работать, если вы не вставить кратной записи сразу:

create or replace rule ct_i_children1 as 
    on insert to Children1 
    do instead (
    insert into Parents(id, attribute1, type) 
     values(nextval('parents_id_seq'), new.attribute1, 'Child1'); 
    insert into Partial_Children1(id, attribute2, type) 
     values(currval('parents_id_seq'), new.attribute2, 'Child1'); 
); 

Тогда вы можете сделать:

insert into Children1 (attribute1, attribute2) values ('a1', 'a2'); 
insert into Children1 (attribute1, attribute2) values ('b1', 'b2'); 

но не

insert into Children1 (attribute1, attribute2) 
    values ('a1', 'a2'), 
     ('b1', 'b2'); 

Так вы действительно не следует использовать систему правил с сложными вызовами currval().

Кроме того, посмотрите на комментарии на этих страницах:

Еще один совет: поддержка в списке PostgreSQL рассылки как превосходна как сам движок базы данных!

И кстати: знаете ли вы, что postgresql имеет поддержку для наследования из коробки?

Резюме: вы должны использовать триггеры или избежать нескольких вставок строк!

+0

Спасибо за ссылки. Они не решают проблему, но по крайней мере я больше не чувствую себя одинокой ;-). Встроенное наследование не совсем предлагало то, что я хотел. –

0

Правила сделают это за вас - они переписывают запрос до его выполнения.

Пока у вас есть фактический стол для базы (Children1), я думаю, вы сможете выполнить одно и то же с TRIGGER вместо ПРАВИЛА.

+0

Извините, но вы полностью прочитали мой вопрос? –

+0

Хм, я думал, что да. Но я думаю, что я, по крайней мере, пропустил часть «не триггеры», извините. –

+0

И правила дают неправильные результаты ;-). –

Смежные вопросы