【问题标题】:Oracle Insert Into Child & Parent TablesOracle 插入子表和父表
【发布时间】:2013-11-19 23:18:55
【问题描述】:

我有一张桌子——我们称之为 MASTER——里面有很多行。现在,我必须创建另一个名为“MASTER_DETAILS”的表,该表将填充来自另一个系统的数据。 Suh 数据将通过 DB Link 访问。

MASTER 与 MASTER_DETAIL 有一个 FK(1 -> 1 关系)。

我创建了一个 SQL 来填充 MASTER_DETAILS 表:

INSERT INTO MASTER_DETAILS(ID, DETAIL1, DETAILS2, BLAH)
    WITH QUERY_FROM_EXTERNAL_SYSTEM AS (
       SELECT IDENTIFIER,
              FIELD1,
              FIELD2,
              FIELD3
       FROM TABLE@DB_LINK 
       --- DOZENS OF INNERS AND OUTER JOINS HERE
    ) SELECT MASTER_DETAILS_SEQ.NEXTVAL, 
             QES.FIELD1, 
             QES.FIELD2, 
             QES.FIELD3
      FROM MASTER M
      INNER JOIN QUERY_FROM_EXTERNAL_SYSTEM QES ON QES.IDENTIFIER = M.ID
      --- DOZENS OF JOINS HERE

上述方法可以很好地将所有值插入 MASTER_DETAILS。

问题是:

在上述方法中,我无法将 MASTER_DETAILS_SEQ.CURRVAL 的值插入到 MASTER 表中。所以我在 DETAILS 表中创建了所有条目,但我没有将它们链接到 MASTER 表。

有没有人只使用 INSERT 语句来解决这个问题?我希望我可以避免使用 LOOPS 和处理此问题的所有内容创建复杂的脚本。

理想情况下,我想做这样的事情:

  INSERT INTO MASTER_DETAILS(ID, DETAIL1, DETAILS2, BLAH) AND MASTER(MASTER_DETAILS_ID)
    WITH QUERY_FROM_EXTERNAL_SYSTEM AS (
       SELECT IDENTIFIER,
              FIELD1,
              FIELD2,
              FIELD3
       FROM TABLE@DB_LINK 
       --- DOZENS OF INNERS AND OUTER JOINS HERE
    ) SELECT MASTER_DETAILS_SEQ.NEXTVAL, 
             QES.FIELD1, 
             QES.FIELD2, 
             QES.FIELD3
      FROM MASTER M
      INNER JOIN QUERY_FROM_EXTERNAL_SYSTEM QES ON QES.IDENTIFIER = M.ID
      --- DOZENS OF JOINS HERE,
      SELECT MASTER_DETAILS_SEQ.CURRVAL FROM DUAL; 

我知道这种方法在 Oracle 上不起作用——但我正在展示这个 SQL 来演示我想要做什么。

谢谢。

【问题讨论】:

  • 您可以创建一个临时触发器,当您从外部系统向 master_detail 表中插入数据时,该触发器将填充您的 MASTER 表。插入所有数据后,您可以删除触发器
  • 这种关系似乎是错误的。如果它只是 1 对 1,那为什么还要有一个单独的表呢?如果必须,为什么主人有FK?如果以后可能是一对多,那么它就行不通了。为什么不单独留下主表,并在明细表中添加(索引)FK,您可以在插入过程中使用M.ID 填充它?我不确定我是否遗漏了什么......

标签: oracle insert


【解决方案1】:

如果两个表之间确实存在一对一的关系,那么它们可以说是一个表。大概你有理由想要将它们分开。也许主表是供应商提供的您不应该触摸的表,而详细信息是额外的数据;但是你无论如何都要通过添加外键字段来改变主人。或者可能会定期重新加载详细信息并且您不想更新主表;但是无论如何您都必须更新外键字段。无论出于何种原因,我都会假设您需要有一个单独的表。

如果您在主表上放置一个引用从表上的主键的外键,您将被限制为只能是一对一的关系。如果确实如此,那么从概念上讲,建立关系的方式并不重要——哪个表具有主键,哪个表具有外键。如果不是这样,那么当您的详细信息表(或远程查询)返回与同一个主表相关的两行时,您的模型将中断 - 即使您确定今天不会发生这种情况,它是否总是正确的?名称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这个方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-06
    • 1970-01-01
    • 2012-11-06
    • 1970-01-01
    • 1970-01-01
    • 2016-11-07
    相关资源
    最近更新 更多