前几天写了一个procedure,然后job调用它,通过dblink从第三方数据库获取数据.写完手动运行了一次,一切正常数据也取到了,但是第二天上班,我却发现了奇怪现象(注job晚上1点执行),我的数据不见了。为此我还调查了,谁动了我的数据,结果是nobody.那结论只有一个问题出现在那个procedure身上了.
procedure主要有两个功能1.从第三方数据库表中取数据;2.对于取来的数据执行完插入之后,要将其重复的数据全部删除(注意是全部删除而不是删掉一条留一条),正是这个两个功能造成我的数据没有了。现将的sql语句贴出来,分析一下
create or replace procedure test_proc is
p_lasttime_stamp date;
--游标export_time_cur获取最后导数据的时间
cursor export_time_cur is .....;
--从上次读取数据的时间点开始再读取数据
cursor export_data_cur is .....
where time_stamp> p_lasttime_stamp;
--删除重复记录的游标
cursor repeat_rec_cur
is ......;
begin
--获取上次导入的时间
open export_time_cur;
if export_time_cur%rowcont=0 then
--则向记录拉取数据的时间戳表中插入一条记录来记录拉取时间戳
insert into ...
commit;
p_lasttime_stamp:=to_date('2000-01-01','yyyy-MM-dd'); --从这个日期开始取数据
else
fetch export_time_cur into p_lasttime_stamp;
end if;
close export_time_cur;
--现在从p_lasttime_stamp开始读这次的数据,全部读过来
for rec in export_data_cur loop
--执行处理及插入
end loop;
commit;
--删除重复的记录
for rec in repeat_rec_cur loop
--执行数据删除
end loop;
commit;
--更新导数据的最后的时间戳
update ..... commit;
EXCEPTION
WHEN OTHERS THEN
raise_application_error(......);
end;
/
这个procedure看起起功能会完善呀,但是问题主要出现在export_time_cur 这个游标上,游标的
%ROWCOUNT属性用来返回迄今为止已经从游标中提取的记录数目。
在游标或游标变量被打开而没有被执行FETCH(提取)之前,%ROWCOUNT的值为0;
这以后,%ROWCOUNT的值就是从结果集中提取出来的记录数,FETCH每成功执行一次
%ROWCOUNT的值就增加一个.
所以现在再来分析一下我写的procedure,就会发现问题,无论何时执行这个procedure,它的拉取数据日期都是
p_lasttime_stamp:=to_date('2000-01-01','yyyy-MM-dd');
然后拉取来的数据就会有大量的重复,如果从job上次执行到下一次执行(第二天早上1点),第三方数据库没有更新一条记录,则这两次取到的数据完全一样(现在是2011年了),而后面的我的procedure则要干的一件事就是干掉自本次拉取的重复数据,这样阴差阳错,拉过来的数据则会被全部干掉,所以表中一条数据也没有了.所以知道问题的所在说可以修改了.所以这就是知其然而不知其所以然的我犯的一个错,要记住下次不能再犯了,现将cursor的其他属性也罗列在此:(这里用到的表是我业务数据库表)
--1。%FOUND属性
/**当游标或游标变量被打开且在执行FETCH语句之前时,
%FOUND为NULL。其后,如果最后的FETCH语句返回一行记录,
则%FOUND为TRUE,如果FETCH语句没有返回记录,
则%FOUND为FALSE。如下例所示。
*/
-- Created on 2011/4/14 by XIAO-CAINIAO
declare
p_steward_name varchar2(20);
cursor steward_cur is
select s.steward_name from steward s where s.steward_id = '1194';
begin
open steward_cur;
if steward_cur%found is null then
dbms_output.put_line('当前游标mycursor的属性%FOUND为NULL!');
end if;
fetch steward_cur
into p_steward_name;
if steward_cur%FOUND then
dbms_output.put_line('p_steward_name= ' || p_steward_name);
else
dbms_output.put_line('没有从游标mycursor中提取出数据!');
end if;
close steward_cur;
end;
/
--2。%ISOPEN
/**
当游标或游标变量被打开时,%ISOPEN属性为TRUE,
否则%ISOPEN属性为FALSE。
我们可以用这个属性来判断一个游标是否已被打开。
*/
-- Created on 2011/4/14 by XIAO-CAINIAO
declare
p_steward_name varchar2(20);
cursor steward_cur is
select s.steward_name from steward s where s.steward_id = '1194';
begin
open steward_cur;
if steward_cur%ISOPEN then
dbms_output.put_line('cursor mycursor is already opened!');
else
open steward_cur;
end if;
fetch steward_cur
into p_steward_name;
close steward_cur;
dbms_output.put_line('p_steward_name' ||' 在 '|| p_steward_name);
end;
--与下例比较注意其中的区别
declare
p_steward_name varchar2(20);
cursor steward_cur is
select s.steward_name from steward s where s.steward_id = '1194';
begin
--open steward_cur;
if steward_cur%ISOPEN then
dbms_output.put_line('cursor mycursor is already opened!');
else
open steward_cur;
end if;
fetch steward_cur
into p_steward_name;
close steward_cur;
dbms_output.put_line('p_steward_name' ||' 在 '|| p_steward_name);
end;
--3。%NOTFOUND
/**
%NOTFOUND属性和%FOUND属性正好相反。当游标或游标变量被打开但是在执行FETCH语句之前时,
%NOTFOUND也是为NULL。其后,如果最后的FETCH语句返回一行记录,
则%NOTFOUND为FALSE,如果FETCH语句没有返回记录,则%NOTFOUND为TRUE。
该属性在循环从一个游标中提取出数据时很有作用。如下面的例子。
*/
declare
p_steward_name varchar2(20);
cursor steward_cur is
select s.steward_name from steward s where s.steward_id = '1194';
v_classes varchar2(30);
begin
v_classes := '运输98-12';
open steward_cur;
loop
fetch steward_cur
into p_steward_name;
dbms_output.put_line(p_steward_name || ' 在 ' || v_classes);
exit when steward_cur%NOTFOUND;
end loop;
close steward_cur;
end;
/
对于这个例子,你可以会发现dbms_output.put_line(p_steward_name || ' 在 ' || v_classes); 这个输出2次,而steward_cur却只有一条记录,这是为什么呢?这个问题也让我纳了很长时间的闷,直到上次在oracle培训问了teacher才解开,这个是因为pl/sql没有即时flush,可以通过下例来测试一下:
begin
for i in 1..10 loop
dbms_output.put_line('A');
dbms_lock.sleep(1);--休息1秒钟
end loop;
end;
/
在sql/plus下执行,并没有一行一行输出,而是等了10秒一下子输出10行.