Oracle 多表插入和 Blob 外键问题
我们有一个表,我们希望根据特定的源列将其分解为表树。我想尝试使用多列插入,但似乎如果我将一个 blob 插入到子表中,我最终会违反外键约束。
我不认为这违反了有关多的规则-table inserts 但我可能是错的...
我希望有人能给我指出一些关于这里实际发生的事情的更深入的资源,这样我就可以确信任何解决方案都会起作用Oracle 数据库 9i 上 liquibase 变更集的一部分 -> 11克。
希望简化场景
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
当 1=1 时,则进入 dest (pk,type) 值 (pk,type)
当 type='one' 时,则进入 dest_one (pkfk,data) VALUES (pk,content)
当 type='two' 时,则进入 dest_two (pkfk) 值 (pk)
选择 pk,type,utl_raw.cast_to_raw(content) 作为源中的内容,其中输入 ('one','two');
更改表 dest_one 启用约束 XFK1DEST_ONE;
这是我倾向于的解决方案。虽然禁用 Blob 表上的外键似乎使其在我的测试环境(10g - 10.2.0.1.0)中工作,但我不确定是否也应该禁用非 Blob 表上的外键(由于 9i、11g 或其他版本的 10g 的行为方式)。这里的任何资源也将不胜感激。
非常感谢!
We have a single table that we want to break up into a tree of tables based upon a particular source column. I wanted to try using a multi-column insert, but it seems that if I insert a blob into a sub table, I wind up with a foreign key constraint violation.
I don't think this violates the rules about multi-table inserts but I could be wrong...
I am hoping that someone could point me to some more in-depth resources around what is actually going on here, so that I can feel confident that whatever solution will work as part of a liquibase changeset on Oracle databases 9i -> 11g.
Hopefully Simplified Scenario
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)
);
Source contains our original data. dest will be our parent table, with children dest_one and dest_two (which will contain information on things of type 'one' or 'two' respectively). Things of type one have content, but things of type two do not.
The Failed Attempt
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');
As previously mentioned, I wound up with a foreign key constraint violation here. To further illustrate that the blob was the issue I tried two seperate similar queries (below) realizing the one without the blob insert worked, but with the blob insert failed.
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 */
Solution 1 - Traditional Inserts
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';
One option I am considering is going back to multiple seperate insert statements, but unlike how I have stated them here, I'm concerned that I'll have to make sure I write my sub-table inserts to only attempt to insert those rows present in parent dest table... I need to do more research on how Liquibase handles multiple sql statements in the same changeset.
Solution 2 - Temporarily disabling foreign key constraints
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;
This is the solution I'm leaning toward. While disabling the foreign key on my blob table seems to make it work in my test environment (10g - 10.2.0.1.0), I'm not sure if I should also be disabling the foreign key on the non-blob table as well (due to how 9i, 11g, or other versions of 10g may behave). Any resources here too would be appreciated.
Thanks a bunch!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
另一种解决方案是将约束评估推迟到 COMMIT。我怀疑(但不确定)多表插入是以不同于您期望和想要的顺序插入行的。按如下方式重新创建约束:
这将重新创建约束,以便可以推迟它们,并从创建它们的时间开始推迟。然后再次尝试原来的 INSERT。
分享并享受。
Another solution would be to defer the constraint evaluation until COMMIT. I suspect (but am not sure) that the multi-table insert is inserting rows in an order other than the one you expect and want. Recreate your constraints as follows:
This re-creates the constraints so that they can be deferred, and are deferred from the time they're created. Then try your original INSERT again.
Share and enjoy.