PostgreSQL的规则和nextval()/ serial问题(非常PostgreSQL特有的)

j.p*_*.p. 11 postgresql rules

当我使用重写规则将插入一个表拆分为两个其他表的插入时,其中一个插入值具有默认的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;
Run Code Online (Sandbox Code Playgroud)

特定于第一类儿童的数据保留在

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)
);
Run Code Online (Sandbox Code Playgroud)

接下来我定义了一个连接上面两个表的视图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;
Run Code Online (Sandbox Code Playgroud)

最后重写规则我遇到了以下问题:

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');
  );
Run Code Online (Sandbox Code Playgroud)

试图插入数据

insert into Children1 (attribute1, attribute2)
  values ('a1', 'a2'),
         ('b1', 'b2');
Run Code Online (Sandbox Code Playgroud)

产生错误消息

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".
Run Code Online (Sandbox Code Playgroud)

解决此问题的方法是替换重写规则的第二个插入

insert into Partial_Children1(id, attribute2, type)
  select p.id, new.attribute2, p.type
    from Parents p
    where p.attribute1 = new.attribute1
Run Code Online (Sandbox Code Playgroud)

但这依赖于attribute1的唯一性,我不想强​​加它.另一种解决方案是首先将值插入临时表中,然后从那里选择两次以插入到两个表中.但由于性能原因,我不喜欢它.

有没有人有另一个想法如何在两个表中获得相同的默认值(只使用规则而不是触发器)?

Jan*_*ing 6

来自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');
  );
Run Code Online (Sandbox Code Playgroud)

然后你可以这样做:

insert into Children1 (attribute1, attribute2) values ('a1', 'a2');
insert into Children1 (attribute1, attribute2) values ('b1', 'b2');
Run Code Online (Sandbox Code Playgroud)

但不是

insert into Children1 (attribute1, attribute2)
  values ('a1', 'a2'),
         ('b1', 'b2');
Run Code Online (Sandbox Code Playgroud)

所以你真的不应该使用规则系统与棘手的currval()调用.

另外,请查看这些页面上的评论:

另一个提示:postgresql邮件列表的支持与数据库引擎本身一样出色!

顺便说一下:你知道postgresql是否支持开箱即用的继承?

简介:您应该使用触发器或避免多行插入!