可以在Oracle中使用约束完全建模一对一关系吗?换句话说,PARENT实体总是完全具有子实体的n-CHILDREN,并且每个孩子只有一个父母。
将n视为数据库常量。
答案 0 :(得分:5)
即使多个会话正在进行更新,这样做也是正确无误的。如果你尝试使用触发器,你会陷入混乱,而Oracle的声明性约束并不足以表达这一点。
可以按如下方式完成: -
然后,您可以执行一系列插入/更新/删除语句。提交时,物化视图将刷新,如果不满足条件,则此时将出现约束违规错误。
特别值得一提的是,只包括未通过约束的行进入物化视图(HAVING count(ChildId)<> 5),这样就不会浪费任何存储空间。
答案 1 :(得分:3)
建立在管理者“鸡+鸡蛋”点上,您可以创建可延迟的约束,这些约束在提交时间之前未经过验证...这些可能会有所帮助吗?
e.g。
ALTER TABLE AGREEMENTS ADD CONSTRAINT name FOREIGN KEY (column) REFERENCES table (column) DEFERRABLE INITIALLY DEFERRED;
答案 2 :(得分:1)
我不知道怎么做。这是一个古老的问题“首先是鸡还是鸡蛋?”。如果还没有添加子项,如何约束父项?如何在没有父项的情况下添加子项?
您可以创建一个新表,称为“ValidParents”,其父ID只有N个孩子,并与触发器保持同步。
答案 3 :(得分:1)
有一种替代解决方案可以强制每个父级使用只有检查,外键和唯一性约束而没有物化视图的0或n个子项。为此,必须为子项编号并添加包含下一个兄弟的编号的字段。这里有一个n = 5的例子在PostgreSQL中有效,对于其他DBS,它必须适应类型 serial :
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都为null或全部不为null。唯一性约束和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询问的类似问题的答案。
答案 4 :(得分: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
可能有点奇怪,但它确实有效。如果有一对多的一对一,例如为客户提供多个地址,但只有一个帐单地址,则此变体可能很有用。
答案 5 :(得分: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记录。
答案 6 :(得分:0)
您可以使用1:M关系正常创建表,然后在子表上有一个计数列,其中包含一个检查约束,用于确定父项可以存在的子项数,以及对父项的唯一约束ID +计数列。 e.g:
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建议约束的物化视图,或者在应用程序中构建额外的东西(例如“错误”报告)。