【问题标题】:PostgreSQL's rules and nextval()/serial problem (very PostgreSQL-specific)PostgreSQL 的规则和 nextval()/serial 问题(非常 PostgreSQL-specific)
【发布时间】:2010-11-22 09:38:51
【问题描述】:

当我使用重写规则将插入到一个表中拆分为插入到另外两个表中时,其中一个插入值的默认 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 的唯一性,我不想强​​加。另一种解决方案是首先将值插入到临时表中,然后从那里选择两次以插入到两个表中。但由于性能原因,我不喜欢它。

有没有人知道如何在两个表中获得相同的默认值(仅使用规则和 not 触发器)?

【问题讨论】:

    标签: postgresql rules


    【解决方案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');
    

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

    另外看看这些页面上的 cmets:

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

    顺便问一下:你知道 postgresql 支持开箱即用的继承吗?

    总结:你应该使用触发器或避免多行插入!

    【讨论】:

    • 感谢您的链接。他们没有解决问题,但至少我不再感到孤独;-)。内置继承并没有完全提供我想要的。
    【解决方案2】:

    规则会为您做到这一点 - 他们在执行之前重写查询。

    只要你有一个实际的基础表 (Children1),我认为你可以使用 TRIGGER 而不是 RULE 来完成同样的事情。

    【讨论】:

    • 嗯,我以为我做到了。但我想我至少错过了“不触发”部分,对此感到抱歉。
    猜你喜欢
    • 2011-01-12
    • 1970-01-01
    • 2017-11-22
    • 1970-01-01
    • 2015-08-10
    • 2010-12-15
    • 1970-01-01
    • 2011-04-05
    • 1970-01-01
    相关资源
    最近更新 更多