Правила PostgreSQL и проблема nextval () / serial (очень специфичная для PostgreSQL)
когда я использую правило перезаписи, которое разбивает вставку на одну таблицу на вставки в две другие таблицы, где одно из вставленных значений имеет по умолчанию nextval('some_sequence') с той же последовательностью для обеих таблиц, тогда вставленные значения по умолчанию отличаются в двух таблицах. Вероятно, это связано с простой заменой текста правилом перезаписи. Вместо этого я надеялся, что значение по умолчанию будет сначала разрешено, а затем то же значение будет записано в обе таблицы.
здесь пример (как вы, наверное, догадываетесь, я пытаюсь реализовать специализацию / обобщение с помощью правил):
-- 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
но это зависит от уникальности attribute1, которую я не хочу навязывать. Другим решением было бы вставить значения сначала во временную таблицу, а затем выбрать два раза оттуда для вставок в две таблицы. Но мне это не нравится из-за производительности причины.
есть ли у кого-нибудь другая идея, как получить одинаковые значения по умолчанию в обеих таблицах (просто используя правила и не триггеры)?
2 ответов
документы http://www.postgresql.org/docs/8.4/static/rules.html
Он (система правил) изменяет запросы принимать во внимание правила и затем передает измененный запрос в планировщик запросов для планирования и казнь
поэтому он сначала перезаписывает запросы, ничего не выполняя.
вы можете заставить его работать, когда вы не вставляете записи multipe сразу:
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 ().
дополнительно взгляните на комментарии по этим страницы:
- http://www.postgresql.org/docs/8.2/interactive/rules-update.html
- http://archives.postgresql.org/pgsql-sql/2004-10/msg00195.php
- http://archives.postgresql.org/pgsql-general/2009-06/msg00278.php
еще один совет: поддержка в списке рассылки postgresql так же прекрасна, как и сам компонент database engine!
и кстати: знаете ли вы, что postgresql поддерживает наследование из коробки?
Summary: вы должны использовать триггеры или избегать нескольких вставок строк!
правила сделают это за вас - они перепишут запрос до его выполнения.
пока у вас есть актуальное таблицы для базы (Детей1), я думаю, что вы сможете сделать то же самое с триггером, а не правило.