2011-07-23 33 views
2

我有一个关于在PostgreSQL中复制行的问题。我的表层次结构非常复杂,许多表通过外键相互关联。为了简单起见,我将用两张表格来解释我的问题,但请记住,我的实际情况需要更多的复杂性。postgresql - 级联复制/插入

说我有以下两个表:

table A 
(
    integer identifier primary key 
    ... -- other fields 
); 

table B 
(
    integer identifier primary key 
    integer a   foreign key references A (identifier) 
    ... -- other fields 
); 

说A和B持有以下行:

A(1) 

B(1, 1) 
B(2, 1) 

我的问题是:我想在创建行的副本A使B中的相关行也被复制到新行中。这将使:

A(1) -- the old row 
A(2) -- the new row 

B(1, 1) -- the old row 
B(2, 1) -- the old row 
B(3, 2) -- the new row 
B(4, 2) -- the new row 

基本上我寻找一个COPY/INSERT CASCADE

有没有一个简单的技巧来实现这个或多或少的自动?也许通过使用临时表?

我相信,如果我必须写出所有的INSERT INTO ... FROM ...以正确的顺序和内容查询自己,我可能会精神失常。

更新

让我们来回答我的问题;)

我做了一些试奏与PostgreSQL的规则机制,这是我想出了:

首先,表定义:

drop table if exists A cascade; 
drop table if exists B cascade; 

create table A 
(
     identifier    serial     not null    primary key, 
     name     varchar     not null 
); 

create table B 
(
     identifier    serial     not null    primary key, 
     name     varchar     not null, 
     a      integer     not null    references A (identifier) 
); 

接下来,对于每个表,我们创建一个函数和相应的规则,其中tran将UPDATE插入到INSERT中。

create function A(in A, in A) returns integer as 
$$ 
     declare 
       r integer; 
     begin 
       -- A 
       if ($1.identifier <> $2.identifier) then 
         insert into A (identifier, name) values ($2.identifier, $2.name) returning identifier into r; 
       else 
         insert into A (name) values ($2.name) returning identifier into r; 
       end if; 

       -- B 
       update B set a = r where a = $1.identifier; 

       return r; 
     end; 
$$ language plpgsql; 

create rule A as on update to A do instead select A(old, new); 

create function B(in B, in B) returns integer as 
$$ 
     declare 
       r integer; 
     begin 
       if ($1.identifier <> $2.identifier) then 
         insert into B (identifier, name, a) values ($2.identifier, $2.name, $2.a) returning identifier into r; 
       else 
         insert into B (name, a) values ($2.name, $2.a) returning identifier into r; 
       end if; 

       return r; 
     end; 
$$ language plpgsql; 

create rule B as on update to B do instead select B(old, new); 

最后,一些testings:

insert into A (name) values ('test_1'); 
insert into B (name, a) values ('test_1_child', (select identifier from a where name = 'test_1')); 

update A set name = 'test_2', identifier = identifier + 50; 
update A set name = 'test_3'; 

select * from A, B where B.a = A.identifier; 

这似乎是工作相当精细。任何意见?

+0

你只是在寻找一个纯笛卡尔产品吗?如果是这样,你就不必经历麻烦,但可以在不使用ON子句的情况下加入A到B. – Kenaniah

回答

0

这将工作。我注意到你明智地避免的一件事是DO ALSO关于插入和更新的规则。插入和更新也是非常危险的,所以避免几乎所有的成本。

然而,经过进一步的反思,触发器不会变得更糟,并且提供更少的硬角。