2009-08-05 90 views
4

一對一關係可以在Oracle中用約束條件完全建模嗎?換句話說,PARENT實體始終具有子實體的正態子態,每個子實體只有一個父實體。建模一對一關係

考慮n是一個數據庫常量。

回答

5

這樣做,使其即使在多個會話進行更新時也是正確和正確的,這並不容易。如果您嘗試使用觸發器,您會陷入混亂,而Oracle的聲明性約束不足以表達這一點。

這是可以做到如下: -

  1. 創建兩個父和子表物化視圖日誌
  2. 創建物化加入認爲,把它們組合在一起,並計算分組的兒童人數由父母。這必須是REFRESH FAST ON COMMIT
  3. 將約束的物化加入認爲,兒童記錄的計數必須等於「N」(數據庫常數)

然後,您可以做一系列的插入/更新/刪除語句。當你提交時,物化視圖會刷新,如果不滿足條件,那麼你會在那個時候遇到違反約束的錯誤。

訣竅的一個好處是隻包含約束失敗到物化視圖的行(HAVING count(ChildId)<> 5),所以您不會浪費任何存儲空間。

1

我看不出如何。這是一個古老的問題,「先來,雞還是雞蛋?」。當沒有孩子添加時,如何約束父母,以及如何在沒有父母的情況下添加孩子?

您可以創建一個新表,名爲類似於「ValidParents」的表,該表只包含具有N個子級的父ID,並使其與觸發器保持同步。

+0

優秀點。 – JohnFx 2009-08-05 20:37:05

+4

可延遲約束。 – cagcowboy 2009-08-05 21:23:05

+1

用觸發器很難做到。您需要取出表鎖以使其在桌面上的多個同時事務下正常工作。所有的交易都必須排在對方之後,並且表現不佳。 – 2009-08-05 21:31:10

0

這可能不是你想要的,但我確實有一種方法可以做類似的事情。

通常安排一個一對多的是這樣的:

Primary Table: 
primary_id (PK) 
primary_stuff 

Secondary Table: 
secondary_id (PK) 
primary_id (FK) 
secondary_stuff 

的替代,來模擬一個嚴格的一對之一將是:

Primary Table: 
primary_id (PK) 
secondary_id (FK, non-null) 
primary_stuff 

Secondary Table: 
secondary_id (PK) 
secondary_stuff 

這可能是有點奇怪,但它有效。當存在一對一一對一的情況下,這種變化可能會很有用,例如爲客戶提供多個地址,但只有一個賬單地址。

+0

你需要插入孤兒行插入父行 – 2009-08-05 20:53:52

+0

這是正確的,這就是爲什麼插入應在交易之前。 – 2009-08-05 21:00:05

3

基於earler「雞+雞蛋」的觀點,你可以創建可延期的約束,直到提交時間才能驗證......這些可能有幫助嗎?

例如

ALTER TABLE AGREEMENTS ADD CONSTRAINT name FOREIGN KEY (column) REFERENCES table (column) DEFERRABLE INITIALLY DEFERRED; 
0

雞和雞蛋問題的另一種解決方案是使用INSERT ALL。因爲它是一個單一的聲明,它消除了對可延遲的外鍵約束的需要。它還提供了插入確切數量的相關行的機制。其他約束會阻止插入其他行。但是我們需要一個帶有外鍵的輔助表來防止意外刪除感興趣的行。

在此示例中,n = 3。

SQL> create table parent 
    2 (pk_col number not null 
    3  , col1 varchar2(20) 
    4  , constraint par_pk primary key (pk_col) 
    5 ) 
    6/

Table created. 

SQL> 
SQL> create table child 
    2 (pk_col number not null 
    3  , seqno number(1,0) not null 
    4  , col2 date 
    5  , constraint ch_pk primary key 
    6   (pk_col, seqno) 
    7  , constraint ch_par_fk foreign key 
    8   (pk_col) references parent (pk_col) 
    9  , constraint ch_ck check (seqno between 1 and 3) 
10 ) 
11/

Table created. 

SQL> 
SQL> create table child_lock 
    2 (pk_col_1 number not null 
    3  , seqno_1 number(1,0) not null 
    4  , pk_col_2 number not null 
    5  , seqno_2 number(1,0) not null 
    6  , pk_col_3 number not null 
    7  , seqno_3 number(1,0) not null 
    8  , constraint chlk_pk primary key 
    9   (pk_col_1, seqno_1, pk_col_2, seqno_2, pk_col_3, seqno_3) 
10  , constraint chlk_par_1_fk foreign key 
11   (pk_col_1, seqno_1) references child (pk_col, seqno) 
12  , constraint chlk_par_2_fk foreign key 
13   (pk_col_2, seqno_2) references child (pk_col, seqno) 
14  , constraint chlk_par_3_fk foreign key 
15   (pk_col_3, seqno_3) references child (pk_col, seqno) 
16 ) 
17/

Table created. 

SQL> 
SQL> insert all 
    2  into parent values (pk_val, val_1) 
    3  into child values (pk_val, 1, sysdate) 
    4  into child values (pk_val, 2, sysdate+1) 
    5  into child values (pk_val, 3, sysdate+2) 
    6  into child_lock values (pk_val, 1, pk_val, 2, pk_val, 3) 
    7 select 999 as pk_val 
    8   , 'APPLE PIE' as val_1 
    9 from dual 
10/

5 rows created. 

SQL> 
SQL> insert into child values (999, 4, sysdate+4) 
    2/
insert into child values (999, 4, sysdate+4) 
* 
ERROR at line 1: 
ORA-02290: check constraint (APC.CH_CK) violated 


SQL> insert into child values (999, 3, sysdate+4) 
    2/
insert into child values (999, 3, sysdate+4) 
* 
ERROR at line 1: 
ORA-00001: unique constraint (APC.CH_PK) violated 


SQL> insert into child values (999, 2.5, sysdate+4) 
    2/
insert into child values (999, 2.5, sysdate+4) 
* 
ERROR at line 1: 
ORA-00001: unique constraint (APC.CH_PK) violated 


SQL> delete from child 
    2/
delete from child 
* 
ERROR at line 1: 
ORA-02292: integrity constraint (APC.CHLK_PAR_1_FK) violated - child record found 


SQL> 

我接受的解決方案是做作,並且還缺乏靈活性一件小事,但那麼是原來的要求。這也遠非防彈 - 從CHILD_LOCK刪除行,你可以刪除一個或多個CHILD記錄。

0

您可以創建表按正常的1:M的關係,那麼子表上有決定許多孩子如何存在父母,以及在唯一約束的檢查約束的計數列家長ID +數量列。例如:

CREATE TABLE Parent (PID NUMBER PRIMARY KEY); 

CREATE TABLE Child (
    PID NUMBER NOT NULL, 
    Count NUMBER(1) NOT NULL, 
    CONSTRAINT count_check CHECK (Count BETWEEN 1 AND 5), 
    CONSTRAINT parent_child_fk FOREIGN KEY (PID) REFERENCES Parent (PID), 
    CONSTRAINT count_unique UNIQUE (PID, Count)); 

這不能保證的唯一的事情是,每個父母至少有五個孩子;爲了解決這個問題,你可以創建一個物化視圖,如WW所示,或者在應用程序中創建一些額外的東西(例如「錯誤」報告)。

1

有一個替代的解決方案,以強制每個家長都恰好是0或N的兒童在使用只是檢查,外鍵和唯一性約束沒有物化視圖。爲此,必須給孩子編號並添加一個包含下一個兄弟姐妹編號的字段。這裏在PostgreSQL中,這適用於其他DBS一個n = 5的例子有可能適應型串行

create table Tree(
    id serial, 
    parent_id integer not null references Tree(id), 
    child_nr integer check(child_nr between 1 and 5), 
    next_sibling_nr integer, 
    unique (parent_id, child_nr), 
    check(next_sibling_nr in (child_nr+1, child_nr-4)), 
    check(((parent_id is null) and (child_nr is null) and 
    (next_sibling_nr is null)) or ((parent_id is not null) 
    and (child_nr is not null) and (next_sibling_nr is not null))), 
    foreign key (parent_id, next_sibling_nr) references Tree(parent_id, child_nr), 
    primary key (id) 
); 

最後(長)檢查約束確保領域PARENT_ID,child_nr和next_sibling_nr是全部爲空或全部不爲空。唯一性約束和對child_nr字段的檢查注意父母最多有5個孩子。對(parent_id,next_sibling_nr)上的其他檢查約束和外鍵約束確保不少於5個子項。

與命令插入根與自動生成的ID爲1後

insert into Tree (parent_id) 
    values (null); 

一個可以包總是添加的5小孩:

insert into Tree (parent_id, child_nr, next_sibling_nr) 
    values (1, 1, 2), 
     (1, 2, 3), 
     (1, 3, 4), 
     (1, 4, 5), 
     (1, 5, 1); 

將該溶液從答案衍生的類似我問了幾個星期的問題ago

+0

我剛纔看到其他人瞭解父母和孩子要在不同的桌子上,而我認爲他們在同一張桌子上。我的答案適應兩張表並不難。 – 2009-08-07 08:17:56