2009-08-05 147 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