2011-07-23 65 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關於插入和更新的規則。插入和更新也是非常危險的,所以避免幾乎所有的成本。

然而,經過進一步的反思,觸發器不會變得更糟,並且提供更少的硬角。