【问题标题】:Using primary key of a table as foreign key in insert query在插入查询中使用表的主键作为外键
【发布时间】:2021-11-16 01:48:22
【问题描述】:

我正在尝试将数据填充到我的表中。假设我有三张桌子

TABLE_A, TABLE_B, TABLE_C.

TABLE_A 有四列,我目前正在获取此表的值如下:

INSERT INTO TABLE_A(COL_A, COL_B, COL_C, COL_D) 
    SELECT TABLE_A.SQ.nextval, colb AS COL_B, colc AS COL_C, cold as COL_D 
    FROM TABLE_D

现在我想在我的另一个表TABLE_B中使用Table_A的主键作为外键,TABLE_B的主键作为TABLE_C的外键。

用例如下我想从一个表(TableX)中提取数据并将其加载到其他表(TABLE_A,Table_Y,TABLE_Z)中,我们将TableX规范化为三个表,因为我不能确定使用主表旧表的键我正在使用序列来生成主键。

【问题讨论】:

  • 如果你实现了一些ETL 过程,你可能还会问:1)我需要序列吗? 2)我可以使用自然键查找父表的代理主键吗?无论如何,您应该更详细地描述您的用例...
  • 嗨@MarmiteBomber 我已经用用例更新了帖子

标签: sql oracle


【解决方案1】:

RETURNING INTO 子句可用于此目的。下面是一个使用 tablea、tableb 和 tablec 进行单行插入的简单示例。

请注意,这是针对逐行操作的,因此在您的情况下,您必须遍历 table_d。还有一个RETURNING BULK COLLECT INTO 用于填充数组。

create sequence tablea_seq;
create sequence tableb_seq;
create sequence tablec_seq;


create table tablea (
    tablea_id                      number 
                                   constraint tablea_tablea_id_pk primary key,
    c1                             varchar2(10 char)
)
;

create table tableb (
    tablea_id                      number
                                   constraint tableb_tablea_id_fk
                                   references tablea on delete cascade,
    tableb_id                      number 
                                   constraint tableb_tableb_id_pk primary key,
    col1                           varchar2(10 char)
);

create table tablec (
    tableb_id                      number
                                   constraint tablec_tableb_id_fk
                                   references tableb on delete cascade,
    tablec_id                      number 
                                   constraint tablec_tablec_id_pk primary key,
    col1                           varchar2(10 char)
);

DECLARE
  l_tablea_id tablea.tablea_id%TYPE;
  l_tableb_id tableb.tableb_id%TYPE;
BEGIN
  INSERT INTO tablea (tablea_id,c1) VALUES (tablea_seq.NEXTVAL,'some val')
    RETURNING tablea_id INTO l_tablea_id;
  INSERT INTO tableb (tableb_id,tablea_id,col1)
    VALUES (tableb_seq.NEXTVAL,l_tablea_id,'Foobar')
    RETURNING tableb_id INTO l_tableb_id;
  INSERT INTO tablec (tablec_id,tableb_id,col1)
    VALUES (tableb_seq.NEXTVAL,l_tablea_id,'Foobar');
END;
/

【讨论】:

  • 很好的例子 Koen,我可以使用 Select 而不是 Values,我的意思是 INSERT INTO tablea (tablea_id,c1) VALUES (tablea_seq.NEXTVAL,'some val') RETURNING tablea_id INTO l_tablea_id; INSERT INTO tableb (tableb_id,tablea_id,col1) VALUES(SELECT table_Id from table_D) , l_tablea_id,'Foobar') RETURNING tableb_id INTO l_tableb_id; END:但是现在假设我有更多列,那么在这种情况下我该怎么办。
  • 如果你做SELECT ... FROM 而不是 values 你想如何处理下一个插入?假设您在 table_a 中插入 10 行,您会将 a 中的哪个主键值分配给 b 中的每一行?除非你给我一个非常详细的要求,否则我无法回答这个问题。但是当您在单个语句中插入多行时,您肯定需要BULK COLLECT INTO
【解决方案2】:

在同一个操作中?如果是这样,那么您不会直接使用序列值,而是先将其存储到局部变量中,以便您可以将其用作

  • table_a 的主键
  • 插入table_b 时的外键值

table_b 的主键也是如此(随后将用作table_c 中的外键)。

【讨论】:

    【解决方案3】:

    假设这是您的非规范化table_x,由三个父-子级别组成,其中key_n是级别n的*原始键和col_n各自的属性。

    select * from table_x;
     
         KEY_1 COL_1           KEY_2 COL_2           KEY_3 COL_3     
    ---------- ---------- ---------- ---------- ---------- ----------
           100 a                 101 aa                101 aaa       
           100 a                 101 aa                102 aab       
           100 a                 102 ab                103 aba       
           100 a                 102 ab                104 abb       
           200 b                 103 ba                105 baa       
           200 b                 103 ba                106 bab       
           200 b                 104 bb                107 bba       
           200 b                 104 bb                108 bbb  
    

    使用一些分析函数,您可以确定要在哪个级别插入哪一行并分配新的主键。

    这里

    • 在级别 n 上仅插入带有 lev_n_rn = 1 的行

    • 新的IDs 用row_number 顺序分配(没有顺序)

    .

    with norm as (
    select  
     row_number() over (partition by KEY_1 order by KEY_2, KEY_3) lev_1_rn,
     KEY_1, COL_1, 
     row_number() over (partition by KEY_1,KEY_2 order by  KEY_3) lev_2_rn,
     KEY_2, COL_2,
     row_number() over (partition by KEY_1,KEY_2,KEY_3 order by COL_3) lev_3_rn, 
     KEY_3, COL_3
    from table_x)
    select
     lev_1_rn,
     sum(case when lev_1_rn = 1 then 1 end) over (order by KEY_1) id1,
     KEY_1, COL_1, 
     lev_2_rn,
     sum(case when lev_2_rn = 1 then 1 end) over (order by KEY_1, KEY_2) id2,
     KEY_2, COL_2,
     lev_3_rn, 
     sum(case when lev_3_rn = 1 then 1 end) over (order by KEY_1, KEY_2, KEY_3) id3,
     KEY_3, COL_3
    from norm;
    
      LEV_1_RN        ID1      KEY_1 COL_1        LEV_2_RN        ID2      KEY_2 COL_2        LEV_3_RN        ID3      KEY_3 COL_3     
    ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
             1          1        100 a                   1          1        101 aa                  1          1        101 aaa       
             2          1        100 a                   2          1        101 aa                  1          2        102 aab       
             3          1        100 a                   1          2        102 ab                  1          3        103 aba       
             4          1        100 a                   2          2        102 ab                  1          4        104 abb       
             1          2        200 b                   1          3        103 ba                  1          5        105 baa       
             2          2        200 b                   2          3        103 ba                  1          6        106 bab       
             3          2        200 b                   1          4        104 bb                  1          7        107 bba       
             4          2        200 b                   2          4        104 bb                  1          8        108 bbb 
    

    剩下的就是INSERT ALL 的任务,使用上面的查询并为每个级别约束相关的行和属性(pk + fk)

    insert all
    when LEV_1_RN = 1 then
      into table_a (id, key_1, col_1) values(ID1, KEY_1, COL_1)
    when LEV_2_RN = 1 then
      into table_b (id, fk_id,  key_2, col_2) values(ID2, ID1, KEY_2, COL_2) 
    when LEV_3_RN = 1 then  
      into table_c (id, fk_id,  key_3, col_3) values(ID3, ID2, KEY_3, COL_3)
    with norm as (
    select  
     row_number() over (partition by KEY_1 order by KEY_2, KEY_3) lev_1_rn,
     KEY_1, COL_1, 
     row_number() over (partition by KEY_1,KEY_2 order by  KEY_3) lev_2_rn,
     KEY_2, COL_2,
     row_number() over (partition by KEY_1,KEY_2,KEY_3 order by COL_3) lev_3_rn, 
     KEY_3, COL_3
    from table_x)
    select
     lev_1_rn,
     sum(case when lev_1_rn = 1 then 1 end) over (order by KEY_1) id1,
     KEY_1, COL_1, 
     lev_2_rn,
     sum(case when lev_2_rn = 1 then 1 end) over (order by KEY_1, KEY_2) id2,
     KEY_2, COL_2,
     lev_3_rn, 
     sum(case when lev_3_rn = 1 then 1 end) over (order by KEY_1, KEY_2, KEY_3) id3,
     KEY_3, COL_3
    from norm;
    

    产生这个结果

    select * from table_a;
            ID      KEY_1 COL_1     
    ---------- ---------- ----------
             1        100 a         
             2        200 b         
    
    select * from table_b;
            ID      FK_ID      KEY_2 COL_2     
    ---------- ---------- ---------- ----------
             1          1        101 aa        
             2          1        102 ab        
             3          2        103 ba        
             4          2        104 bb 
             
    select * from table_c;        
            ID      FK_ID      KEY_3 COL_3     
    ---------- ---------- ---------- ----------
             2          1        102 aab       
             4          2        104 abb       
             6          3        106 bab       
             8          4        108 bbb  
    

    我假设这将是(对于大表)最有效的转换(因为您省略了循环和查找)。

    如果您维护新表是单用户模式(无多用户),您实际上不需要序列,但如果您愿意,您可以在转换后使用适当的设置初始值部署它们。

    【讨论】:

      猜你喜欢
      • 2017-03-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-21
      相关资源
      最近更新 更多