Правила PostgreSQL и NextVal ()/Serial Place (очень специфичная для PostgreSQL)

StackOverflow https://stackoverflow.com/questions/1324377

  •  19-09-2019
  •  | 
  •  

Вопрос

Когда я использую правило перезаписывания, которое разбивает вставку в одну таблицу в вставки в две другие таблицы, в которых одно из вставленных значений имеет в качестве по умолчанию 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)
);

Затем я определил представление детей1, которая присоединяется к обеим таблицам выше (я переписал представление, явно заявив, что 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, которую я не хочу навязывать. Другим решением было бы вставить значения сначала во временную таблицу, а затем дважды выбрать оттуда для вставки в две таблицы. Но мне это не нравится из -за причин производительности.

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

Это было полезно?

Решение

От документов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');

Таким образом, вы действительно не должны использовать систему правил с помощью хитрых вызовов Curval ().

Кроме того, взгляните на комментарии на этих страницах:

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

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

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

Другие советы

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

Пока у вас есть настоящая таблица для базы (дети1), я думаю, что вы сможете выполнить то же самое с триггером вместо правила.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top