首页 文章

多表插入和Blob的Oracle外键问题

提问于
浏览
2

我们有一个表,我们希望根据特定的源列分解为表树 . 我想尝试使用多列插入,但似乎如果我将blob插入子表中,我最终会遇到外键约束违规 .

我不认为这违反了rules about multi-table inserts但我可能是错的......

我希望有人可以向我指出一些关于实际情况的更深入的资源,这样我就可以确信任何解决方案都可以作为Oracle数据库9i上的liquibase变更集的一部分 - > 11g .

希望简化的场景

CREATE TABLE source (
    pk NUMBER NOT NULL PRIMARY KEY,
    type VARCHAR2(20) NOT NULL,
    content VARCHAR2(20) NOT NULL
);

INSERT INTO source (pk,type,content) values (1,'two','n/a');
INSERT INTO source (pk,type,content) values (2,'one','Content');

CREATE TABLE dest (
    pk NUMBER NOT NULL PRIMARY KEY,
    type VARCHAR2(20) NOT NULL
);


CREATE TABLE dest_one  (
    pkfk NUMBER NOT NULL PRIMARY KEY,
    data BLOB NOT NULL,
    CONSTRAINT XFK1DEST_ONE FOREIGN KEY (pkfk) REFERENCES dest (pk)
);


CREATE TABLE dest_two  (
    pkfk NUMBER NOT NULL PRIMARY KEY,
    CONSTRAINT XFK1DEST_TWO FOREIGN KEY (pkfk) REFERENCES dest (pk)
 );

来源包含我们的原始数据 . dest将是我们的父表,子目录为dest_one和dest_two(它们将分别包含“one”或“two”类型的信息) . 第一类的东西有内容,但第二类的东西没有 .

失败的尝试

INSERT ALL
WHEN 1=1 THEN INTO dest (pk,type) VALUES (pk,type)
WHEN type='one' THEN INTO dest_one (pkfk,data) VALUES (pk,content)
WHEN type='two' THEN INTO dest_two (pkfk) VALUES (pk)
SELECT pk,type,utl_raw.cast_to_raw(content) as content from source where type in ('one','two');

如前所述,我在这里结束了外键约束违规 . 为了进一步说明blob是问题,我尝试了两个单独的类似查询(下面),实现了没有blob插入工作的那个,但是blob插入失败了 .

INSERT ALL
WHEN 1=1 THEN INTO dest (pk,type) VALUES (pk,type)
WHEN type='two' THEN INTO dest_two (pkfk) VALUES (pk)
SELECT pk,type,utl_raw.cast_to_raw(content) as content from source where type = 'two';
/* Successful */

INSERT ALL
WHEN 1=1 THEN INTO dest (pk,type) VALUES (pk,type)
WHEN type='one' THEN INTO dest_one (pkfk,data) VALUES (pk,content)
SELECT pk,type,utl_raw.cast_to_raw(content) as content from source where type = 'one';
/* ORA-02291: integrity constraint violated, no parent key */

解决方案1 - 传统插入

INSERT INTO dest (pk,type) SELECT pk,type from source where type in ('one','two');
INSERT INTO dest_two (pkfk) SELECT pk from source where type = 'two';
INSERT INTO dest_one (pkfk,data) SELECT pk,utl_raw.cast_to_raw(content) from source where type = 'one';

我正在考虑的一个选项是回到多个单独的插入语句,但不像我在这里说的那样,我担心我必须确保编写我的子表插入只尝试插入那些存在的行在父目录表中......我需要对Liquibase如何处理同一变更集中的多个sql语句进行更多研究 .

解决方案2 - 暂时禁用外键约束

ALTER TABLE dest_one DISABLE CONSTRAINT XFK1DEST_ONE; 

 INSERT ALL
WHEN 1=1 THEN INTO dest (pk,type) VALUES (pk,type)
WHEN type='one' THEN INTO dest_one (pkfk,data) VALUES (pk,content)
WHEN type='two' THEN INTO dest_two (pkfk) VALUES (pk)
SELECT pk,type,utl_raw.cast_to_raw(content) as content from source where type in ('one','two'); 

 ALTER TABLE dest_one ENABLE CONSTRAINT XFK1DEST_ONE;

这是我倾向于的解决方案 . 虽然禁用我的blob表上的外键似乎使它在我的测试环境(10g - 10.2.0.1.0)中工作,但我不确定是否也应该禁用非blob表上的外键(由于9i,11g或其他版本的10g可能表现如何) . 这里的任何资源也将受到赞赏 .

谢谢你!

1 回答

  • 2

    另一种解决方案是将约束评估推迟到COMMIT . 我怀疑(但不确定)多表插入是按照您期望和想要的顺序插入行 . 重新创建约束,如下所示:

    ALTER TABLE DEST_ONE DROP CONSTRAINT XFK1DEST_ONE;
    
    ALTER TABLE DEST_ONE
      ADD CONSTRAINT XFK1DEST_ONE
        FOREIGN KEY (pkfk) REFERENCES dest (pk) 
        INITIALLY DEFERRED DEFERRABLE;
    
    ALTER TABLE DEST_TWO DROP CONSTRAINT XFK1DEST_TWO;
    
    ALTER TABLE DEST_TWO
      ADD CONSTRAINT XFK1DEST_TWO
        FOREIGN KEY (pkfk) REFERENCES dest (pk)
        INITIALLY DEFERRED DEFERRABLE;
    

    这将重新创建约束,以便它们可以延迟,并从创建它们的时间开始推迟 . 然后再次尝试原始的INSERT .

    分享和享受 .

相关问题