如果两个表之间确实存在一对一的关系,那么它们可以说是一个表。大概你有理由想要将它们分开。也许主表是供应商提供的您不应该触摸的表,而详细信息是额外的数据;但是你无论如何都要通过添加外键字段来改变主人。或者可能会定期重新加载详细信息并且您不想更新主表;但是无论如何您都必须更新外键字段。无论出于何种原因,我都会假设您需要有一个单独的表。
如果您在主表上放置一个引用从表上的主键的外键,您将被限制为只能是一对一的关系。如果确实如此,那么从概念上讲,建立关系的方式并不重要——哪个表具有主键,哪个表具有外键。如果不是这样,那么当您的详细信息表(或远程查询)返回与同一个主表相关的两行时,您的模型将中断 - 即使您确定今天不会发生这种情况,它是否总是正确的?名称master_details 的复数形式表明这可能是意料之中的。也许。以另一种方式建立关系可以防止这成为一个问题。
我猜你决定把关系放在这样的地方,这样你就可以使用详细信息的键加入表格:
select m.column, md.column
from master m
join master_details md on md.id = m.detail_id
... 因为您希望这是最快的方式,因为 md.id 将被索引(隐式地,作为主键)。但是您可以通过将主 ID 作为外键添加到详细信息表中来达到相同的效果:
select m.column, md.column
from master m
join master_details md on md.master_id = m.id
无论如何,索引外键是一种很好的做法,只要您在master_details.master_id 上有一个索引,那么性能应该是相同的(或多或少,其他因素可能会起作用,但我希望这一点一般情况下是这样)。这也将允许将来有多个详细记录,而无需修改架构。
举个简单的例子,假设您创建了一个主表并填充了一些虚拟数据:
create table master(id number, data varchar2(10),
constraint pk_master primary key (id));
create sequence seq_master start with 42;
insert into master (id, data)
values (seq_master.nextval, 'Foo ' || seq_master.nextval);
insert into master (id, data)
values (seq_master.nextval, 'Foo ' || seq_master.nextval);
insert into master (id, data)
values (seq_master.nextval, 'Foo ' || seq_master.nextval);
select * from master;
ID DATA
---------- ----------
42 Foo 42
43 Foo 43
44 Foo 44
您提出的更改可能如下所示:
create table detail (id number, other_data varchar2(10),
constraint pk_detail primary key(id));
create sequence seq_detail;
alter table master add (detail_id number,
constraint fk_master_detail foreign key (detail_id)
references detail (id));
insert into detail (id, other_data)
select seq_detail.nextval, 'Foo ' || seq_detail.nextval
from master m
-- joins etc
;
...加上主外键的更新,这是您正在努力解决的问题,所以现在让我们手动完成:
update master set detail_id = 1 where id = 42;
update master set detail_id = 2 where id = 43;
update master set detail_id = 3 where id = 44;
然后你会查询为:
select m.data, d.other_data
from master m
join detail d on d.id = m.detail_id
where m.id = 42;
DATA OTHER_DATA
---------- ----------
Foo 42 Bar 1
Plan hash value: 2192253142
------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 22 | 2 (0)| 00:00:01 |
| 1 | NESTED LOOPS | | 1 | 22 | 2 (0)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID| MASTER | 1 | 13 | 1 (0)| 00:00:01 |
|* 3 | INDEX UNIQUE SCAN | PK_MASTER | 1 | | 0 (0)| 00:00:01 |
| 4 | TABLE ACCESS BY INDEX ROWID| DETAIL | 3 | 27 | 1 (0)| 00:00:01 |
|* 5 | INDEX UNIQUE SCAN | PK_DETAIL | 1 | | 0 (0)| 00:00:01 |
------------------------------------------------------------------------------------------
如果你交换关系周围的变化变成:
create table detail (id number, master_id, other_data varchar2(10),
constraint pk_detail primary key(id),
constraint fk_detail_master foreign key (master_id)
references master (id));
create index ix_detail_master_id on detail (master_id);
create sequence seq_detail;
insert into detail (id, master_id, other_data)
select seq_detail.nextval, m.id, 'Bar ' || seq_detail.nextval
from master m
-- joins etc.
;
不需要更新主表,查询变为:
select m.data, d.other_data
from master m
join detail d on d.master_id = m.id
where m.id = 42;
DATA OTHER_DATA
---------- ----------
Foo 42 Bar 1
Plan hash value: 4273661231
----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 19 | 2 (0)| 00:00:01 |
| 1 | NESTED LOOPS | | 1 | 19 | 2 (0)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID| MASTER | 1 | 10 | 1 (0)| 00:00:01 |
|* 3 | INDEX UNIQUE SCAN | PK_MASTER | 1 | | 0 (0)| 00:00:01 |
| 4 | TABLE ACCESS BY INDEX ROWID| DETAIL | 1 | 9 | 1 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | IX_DETAIL_MASTER_ID | 1 | | 0 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------
计划中唯一真正的区别是您现在有范围扫描而不是唯一扫描;如果您确实确定它是 1 对 1,则可以使索引唯一,但没有太多好处。
SQL Fiddle这个方法。