使用 pl/sql 函数插入多个表会导致外键冲突
Posted
技术标签:
【中文标题】使用 pl/sql 函数插入多个表会导致外键冲突【英文标题】:Insert into multiple tables utilizing pl/sql function results in foreign key violation 【发布时间】:2012-08-09 15:25:37 【问题描述】:DECLARE
cursor curs is select * from starting;
appleId number;
bananaId number;
BEGIN
for foo in curs
LOOP
insert into apple (id, weight)
values(1,1)
returning id into appleId;
insert into banana(id,weight)
values(1,3)
returning id into bananaId;
insert into apple_banana_lookup
values(1,appleId,bananaId);
END LOOP;
COMMIT;
END;
以上代码导致外键约束违反。声称Apple
中的ID
字段尚不存在。我的问题是如何使上面的代码功能并使 apple_banana_lookup
表成功保留 appleId
和 bananaId
中引用的键。作为补充,我希望避免在每次插入 apple
和 banana
后都必须提交,因为给定游标中将有大约 2 亿条记录。
更新
架构声明:
create table apple
(
id number(20,0) not null,
weight number (20,0)
);
create table banana
(
id number(20,0) not null,
weight number(20,0)
) ;
create table apple_banana_lookup
(
id number(20,0) not null,
appleId number(20,0) not null,
bananaId number(20,0) not null
CONSTRAINT "apple_fk" foreign key ("appleId")
REFERENCES "apple" ("id"),
CONSTRAINT "banana_fk" foreign key ("bananaId")
REFERENCES "banana" ("id"),
);
错误信息:
ORA-02291: integrity constraint
parent key not found
【问题讨论】:
你能在第二次插入后试试commit
。
@Annjawn 我已经尝试过了,它会导致同样的错误
id
in apple_banana_lookup
不应该是必要的,因为人们会假设元组 [appleId
, bananaId
] 是独一无二的......而且 2 亿行似乎很多在一个事务中处理 - 根据许多其他因素,您的系统可能只是锁定表。另外,为什么是游标?当 SQL 真正意味着处理集合时,它们往往表示命令式思维。您可以只使用一组标准的INSERT
s 吗?
@X-Zero 如果我这样做,我最终会从starting
表中读取三个表。因为我必须取 apple
和 starting
和 banana
和 starting
之间的析取,然后从我的查找表中取析取
好的,我明白你的意思了。性能节省有那么大吗?我原以为使用游标会将其变成 RBAR(逐行痛苦)情况。
【参考方案1】:
您的解释中似乎遗漏了某些内容——正确创建表后,您的代码似乎可以正常工作
我假设这就是 apple
、banana
和 apple_banana_lookup
表的定义方式(请注意,由于您没有在插入 apple_banana_lookup
时指定列列表,所以我假设表中的列是按照您的 PL/SQL 块所期望的那样排序的)。
SQL> create table apple(
2 id number primary key,
3 weight number
4 );
Table created.
SQL> create table banana(
2 id number primary key,
3 weight number
4 );
Table created.
SQL> create table apple_banana_lookup (
2 id number primary key,
3 appleID number references apple(id),
4 bananaID number references banana(id)
5 );
Table created.
为了避免对您的代码进行任何更改,我创建了一个有 1 行的 starting
表
SQL> create table starting( id number );
Table created.
SQL> insert into starting values( 1 );
1 row created.
现在我完全按照您发布的方式运行您的代码。不产生错误,每个表中插入一行。
SQL> DECLARE
2 cursor curs is select * from starting;
3 appleId number;
4 bananaId number;
5 BEGIN
6 for foo in curs
7 LOOP
8 insert into apple (id, weight)
9 values(1,1)
10 returning id into appleId;
11 insert into banana(id,weight)
12 values(1,3)
13 returning id into bananaId;
14 insert into apple_banana_lookup
15 values(1,appleId,bananaId);
16 END LOOP;
17 COMMIT;
18 END;
19 /
PL/SQL procedure successfully completed.
SQL> select * from apple_banana_lookup;
ID APPLEID BANANAID
---------- ---------- ----------
1 1 1
【讨论】:
嗯,我可能错过了一些转录隐藏名字的东西,让我检查一下。【参考方案2】:create table apple
(
id number(20,0) not null,
weight number (20,0)
); --No primary keys here
create table banana
(
id number(20,0) not null,
weight number(20,0)
) ; -- No primary keys here
create table apple_banana_lookup
(
id number(20,0) not null,
appleId number(20,0) not null,
bananaId number(20,0) not null,
CONSTRAINT apple_fk foreign key (appleId)
REFERENCES apple(id), --this wont work
CONSTRAINT banana_fk foreign key (bananaId)
REFERENCES banana(id) --this wont work
);
看起来上面创建apple_banana_lookup
的语句不起作用。表apple_banana_lookup
上的引用完整性需要引用引用表上的唯一键。应该是这样的
create table apple
(
id number(20,0) not null,
weight number (20,0) ,
CONSTRAINT apple_pk PRIMARY KEY (id)
);
create table banana
(
id number(20,0) not null,
weight number(20,0) ,
CONSTRAINT banana_pk PRIMARY KEY (id)
) ;
create table apple_banana_lookup
(
id number(20,0) not null,
appleId number(20,0) not null,
bananaId number(20,0) not null,
CONSTRAINT app_ban_pk PRIMARY KEY (appleId, bananaId),
CONSTRAINT apple_fk foreign key (appleId)
REFERENCES apple(id),
CONSTRAINT banana_fk foreign key (bananaId)
REFERENCES banana(id)
);
所以有了这个,下面的工作就像一个魅力(除了循环之外和你的一样)-
SQL> DECLARE
appleId number;
bananaId number;
BEGIN
insert into apple (id, weight)
values(1,1)
returning id into appleId;
insert into banana(id,weight)
values(1,3)
returning id into bananaId;
insert into apple_banana_lookup
values(1,appleId,bananaId);
COMMIT;
END;
/
PL/SQL block completed successfully.
SQL> select * from apple;
ID WEIGHT
-------------------- --------------------
1 1
SQL> select * from banana;
ID WEIGHT
-------------------- --------------------
1 3
SQL> select * from apple_banana_lookup;
ID APPLEID BANANAID
-------------------- -------------------- --------------------
1 1 1
【讨论】:
我不确定你的意思是不正确的。我用改名写了我的 oracle DDL 中的内容 如果你在外键子句中引用apple(id)
,那么apple
表id
列应该是唯一的主键,同样适用于banana(id)
【参考方案3】:
AFAIR 数据在事务中始终可见,因此您应该能够引用在之前的 INSERT 语句中插入的数据。 我怀疑该错误是指字段名称,而不是字段值。如果您向我们提供了有关错误的更多详细信息,那么我们将能够提供更好的帮助。 还要检查你的约束源。
【讨论】:
以上是关于使用 pl/sql 函数插入多个表会导致外键冲突的主要内容,如果未能解决你的问题,请参考以下文章