事务处理呈现挂起(不能提交)的状态怎么解决!(100分)

  • 主题发起人 主题发起人 lipingcool
  • 开始时间 开始时间
L

lipingcool

Unregistered / Unconfirmed
GUEST, unregistred user!
我有一段对数据库进行事务处理的程序,但是事务开始后,程序就呈现挂起状态,事务也提交
不了。该怎么解决这个问题,小弟实在是没办法了,工程催得很急,望各位多多指教!

Database.StartTransaction;
try

//外层循环
if all_user_query.RecordCount>0 then
begin
all_user_query.First;
while not all_user_query.EOF do
begin
//这里是具体对数据库操作的操作语句,有update,select
all_user_query.next;
end;

Database.Commit;
except
Database.Rollback;
showmessage('ERROR!');
raise;
end;

我头痛的是:我把事务放到外层循环外面就会出现事务挂起,不能提交的情况。但假如我把
事务放到外层循环里去的时候,就没有问题!我的本意是当循环里面出现数据库错误的时候,
回滚所有的操作,而不是放到循环里去回滚其中一次的操作!

是不是有什么数据库操作不能包到Database.StartTransaction里去呢?



 
我有一个建议
用数据库服务器的存储过程来实现事务处理
我个人比较偏好这种方法,也不知到到第哪个效率高
但我决的这样可以让你的事务流程看起来简单一点
对于处理过程中如果有比较复杂的判断
我的处理办法是在delphi中先判断出来,然后用参数传进去
 
你要看是哪一句SQL执行时挂起。估计是你操作的表未提交,后面的操作又去操作
造成的。把数据库操作设成DIRTYREAD试一试。
 
现在的数据库都不能做到资源共享冲突时提示用户是否要结束一个或多个事物,当一个事物
要读取前一个事物涉及到的数据时,就挂在那了,第一个不结束,后一个就怎也结束不了。

所以建议你还是一个一个的来Commit,不要囫囵吞枣一把抓,不然就象你看的那样,
都挂那了。
 
大家的意见很一致嘛。你再试试!
 
如果是文件型数据库
事务中的修改
PARDOX不能超过250条
FOXPRO不能超过100条
而且必须设为脏读
 
我用的SQL SERVER7!
还是出现这样的问题:我把事务放到外层循环外面就会出现事务挂起,不能提交的情况。但假如我把事务放到外层循环里去的时候,就没有问题!
我的本意是当循环里面出现数据库错误的时候,回滚所有的操作,而不是放到循环里去回滚其中一次的操作!
 
我觉得您对事务处理没有理解,真正的方法是:
procedure save
begin
database.StartTransaction
try
while true do
begin
end
except
database.rollback
exit //退出procedure or Function
end;
data_base.commit
end;
这样才不会出错,而且也实现你的本意。
否则系统将会
 
to micky:
如你所说,把commit放到了except之后,现在数据库是不会出现挂起的现象了,但仍然不会回滚所有的操作,
还是只回滚了循环中的出错的这一次对数据库操作! :(
因我这是一个计费系统,想达到出错就回滚所有操作的目的!
另请问,为什么要把commit放到最后?在许多资料上都是把commit放到except之前的啊?能否
把真正的事务处理阐述一下,不胜感激!
 
具体说可能是您的程序编写的问题
比如不要使用POST方法,不要使用Cache,另外
是不是dataset控件都属于Database?
不知道您是否完全了解事物处理,
否则应该看一个参考历程。
 
to micky:
程序中未显式调用POST方法,未使用缓存更新.
另外,所有的dataset控件都属于Database!


 
我说,
您能发个例子吗?
独立的一个,
让我看看
mickyb@online.sh.cn
 
add the following before your procedure
if not Database.InTransaction then //

 
To lipingcool:
把事务放到最外层的做法是对的。
问题的症结在于,你很可能在事务当中又对本事务未提交的数据产生了依赖。
解决问题的关键是把所有的修改操作缩减,在事务中间循环交互是数据库编程的大忌,
你的要求一般可以写成 Update ... From ... 的形式:
Update T1 Set T1.XXX=??? ,...
From T1 Inner Join T2 On (T1.XXX=T2.XXX)
因为你的两个数据集一定会有天然的联系的。
 
以下是我的程序的主要部分,这是一个计费程序,我的本意是当有一个用户的计费出现问题的时候,
则将所有已完成计费的用户的记录回滚,但现在只能做到回滚出错用户的记录,这不是我的本意,
请各位多多指教!

//******具体的计费过程******
procedure Tbill_web800_charge_win.pr_base_rate_bill(from_dt:string;end_dt:string);
var
ls_from_dt,ls_end_dt:string; //计费起始,结束时间
ls_user_account:string; //用户帐号
ls_charge:string; //存放总的通话费用
ls_rent_fee:string; //存放月租费
begin
ls_from_dt:=from_dt; //计费起始时间
ls_end_dt:=end_dt; //计费结束时间

//取得所有要计费的用户
all_user_query.close;
all_user_query.sql.Clear;
all_user_query.sql.Add('select * from t_service_web800 where service_status=''2'' order by user_account');
all_user_query.open;

//开始事务处理
data_win.Database.StartTransaction;
try

//对每个用户开始计费
if all_user_query.RecordCount>0 then
begin
all_user_query.First;
while not all_user_query.EOF do
begin
//取得用户帐号
ls_user_account:=all_user_query.fieldbyname('user_account').asstring;

//计算这个用户的月租费,将结果防在ls_rent_fee中
ls_rent_fee=计算出来的值

//对每个用户的所有通话记录进行计费
bill_all_record_query.close;
bill_all_record_query.sql.Clear;
bill_all_record_query.sql.Add('select sum(charge) as sum_charge from t_record_web800 where (user_account='''+ls_user_account+''') and (start_dt between '''+ls_from_dt+''' and '''+ls_end_dt+''')');
bill_all_record_query.open;

ls_charge:=bill_all_record_query.fieldbyname('sum_charge').asinteger);

//插入用户应交费记录
insert_query.close;
insert_query.sql.Clear;
insert_query.sql.Add('insert into t_bill_web800_charge (user_account,from_dt,end_dt,rent_fee,talk_fee,late_fee,left_coins) values ('''+ls_user_account+''','''+ls_from_dt+''','''+ls_end_dt+''','''+ls_rent_fee+''','''+ls_charge+''',0,0)');
insert_query.ExecSQL;

//移到下一条记录(下一个用户)
all_user_query.Next;
end;
end
else
begin
bill_progressbar.Position:=bill_progressbar.max;
end;

//提交事务,若抛出异常,则回滚所有对数据库的操作
except
data_win.Database.Rollback;
application.MessageBox(pchar('计费失败!可能是由以下原因造成:'+#13+#13'1.该用户('+ls_user_account+')已经不存在'+#13+'2.计费时间段不能重复计费'),'错误提示',mb_ok);
raise;
exit;
end;

data_win.Database.Commit;
end;
 
为什么不试试我说的存储过程呢?
 
to 小猪:
我也想用存储过程,只可惜,我的操作是要从查询返回的数据集的每一行中提取信息,要用存储
过程来实现的话,就得用Cursor,众所周知,对于大数据量来说,用Cursor是一种灾难!
 
首先你说放在外面你的事务无法建立,你可以自己在建立前try database1.commit
其次我觉得你的程序中
all_user_query.close;
all_user_query.sql.Clear;
all_user_query.sql.Add('select * from t_service_web800 where service_status=''2'' order by user_account');
all_user_query.open;
最好也在建立事务之后再写。
//另外我写过类似的,内部操作的东西更复杂,但是我没有出现你说的问题
 
to onedot:
现在数据库是不会出现挂起的现象了,但仍然不会回滚所有的操作,还是只回滚了循环中
出错的这一次对数据库操作!
我程序中的这段是用于取回要处理的数据集,它是在事务的外面
all_user_query.close;
all_user_query.sql.Clear;
all_user_query.sql.Add('select * from t_service_web800 where service_status=''2'' order by user_account');
all_user_query.open;
我发现出现我所说的情况,应该是我对事务的操作不对所造成的,正确的应该是怎样做呢?
 
哦,对了,你说的问题应该在每个SQL操作后都应该
try
your_query.execsql;//你的SQL操作执行;但不要写COMMIT
except
showmessage('error');
datamodule3.database1.rollback;
exit;
end;
如果是在循环里,应该跳出循环。
 
后退
顶部