在 oracle 中使用游标插入和更新记录
Posted
技术标签:
【中文标题】在 oracle 中使用游标插入和更新记录【英文标题】:INSERT and UPDATE a record using cursors in oracle 【发布时间】:2012-08-08 22:47:22 【问题描述】:我有 2 个表 - student
和 studLoad
都有 2 个字段 studID
和 studName
。我想将student
表中的数据加载到stuLoad
表中。
如果数据已经存在于studLoad
表中,那么应该更新它,否则应该插入它。以下是我的代码:
create or replace procedure studentLoad is
v_id student.studID%type;
v_name student.studName%type;
v_sn studLoad.studName%type;
cursor cur_load is
select * from student;
begin
open cur_load;
loop
fetch cur_load into v_id,v_name;
exit when cur_load%notfound;
select studName into v_sn from studLoad where studID = v_id;
if(v_sn!= v_name) then
update studLoad set studName= v_name where studID= v_id;
else
insert into studLoad values(v_id,v_name);
dbms_output.put_line(v_id || ' ' || v_name);
end if;
end loop;
close cur_load;
end;
它不工作。 studLoad 表中的行未更新。我该如何解决这个问题?在 SQL Server 中,我们使用IF EXISTS(select...from stuLoad..)
来检查表中是否存在记录,在 Oracle 中有没有办法做同样的事情?如果是,请让我知道。
【问题讨论】:
Oracle: how to UPSERT (update or insert into a table?)的可能重复 您的代码无法正常工作,因为它没有按照您所说的进行操作。我很惊讶为什么它不会抛出异常。 NO_DATA_FOUND 记录不匹配时的异常。评估是否插入记录的 IF 条件是错误的,因为它只会插入新的重复项。如果 studId 在 studLoad 和 student 表中都是唯一的,Ben 提出的解决方案可以正常工作。 【参考方案1】:这是一种非常低效的方法。您可以使用merge
语句,然后就不需要游标、循环或(如果可以不用的话)PL/SQL。
MERGE INTO studLoad l
USING ( SELECT studId, studName FROM student ) s
ON (l.studId = s.studId)
WHEN MATCHED THEN
UPDATE SET l.studName = s.studName
WHERE l.studName != s.studName
WHEN NOT MATCHED THEN
INSERT (l.studID, l.studName)
VALUES (s.studId, s.studName)
确保您commit
,一旦完成,以便能够在数据库中看到它。
要真正回答您的问题,我会这样做如下。这样做的好处是可以在 SQL 中完成大部分工作,并且只根据 rowid(表中的唯一地址)进行更新。
它声明了一种类型,您可以将数据批量放入其中,一次 10,000 行。然后分别处理这些行。
但是,正如我所说,这不会像 merge
那样高效。
declare
cursor c_data is
select b.rowid as rid, a.studId, a.studName
from student a
left outer join studLoad b
on a.studId = b.studId
and a.studName <> b.studName
;
type t__data is table of c_data%rowtype index by binary_integer;
t_data t__data;
begin
open c_data;
loop
fetch c_data bulk collect into t_data limit 10000;
exit when t_data.count = 0;
for idx in t_data.first .. t_data.last loop
if t_data(idx).rid is null then
insert into studLoad (studId, studName)
values (t_data(idx).studId, t_data(idx).studName);
else
update studLoad
set studName = t_data(idx).studName
where rowid = t_data(idx).rid
;
end if;
end loop;
end loop;
close c_data;
end;
/
【讨论】:
为了限制更改的数量(随之减少撤消空间、重做日志和执行时间),我建议在两个合并语句的更新中添加一个条件 new_studName != old_studName ("where l .studName = s.studName" docs.oracle.com/cd/E11882_01/server.112/e26088/…) 和 PL/SQL 块中(可以通过不同的方式完成,更好的方法是在光标)。 @AlessandroRossi,你当然是正确的。我已经更新了。 请查看线程***.com/questions/11924657/merge-when-matched-insert。我希望执行建设性的负载。我如何使用合并来做到这一点? 如何调用这个插入到过程中的合并代码?开始 proc_name;结尾;在这里不起作用【参考方案2】:如果您想使用您的程序,请考虑更改一些行:
create or replace procedure studentLoad is
v_id student.studID%type;
v_name student.studName%type;
v_sn studLoad.studName%type;
cursor cur_load is
select * from student;
begin
open cur_load;
loop
fetch cur_load into v_id,v_name;
exit when cur_load%notfound;
begin
select studName into v_sn from studLoad where studID = v_id;
if(v_sn!= v_name) then
update studLoad set studName= v_name where studID= v_id;
end if;
exception
when no_data_found then
insert into studLoad values(v_id,v_name);
end;
dbms_output.put_line(v_id || ' ' || v_name);
end loop;
close cur_load;
end;
我认为它应该可以工作,没有测试它。
【讨论】:
以上是关于在 oracle 中使用游标插入和更新记录的主要内容,如果未能解决你的问题,请参考以下文章