无法为更新定位行。一些值可能已在最后一次读取后已更改解决办法
(2011-12-20 14:08:53)
标签:
杂谈 |
分类: delphi |
在Delphi中,如果使用ADOQuery插入数据没有问题,
之后对数据进行修改保存时,就会遇到“无法为更新定位行,一些值可能已在最后一次读取后已更改”的问题。
原因有这样几种:
1.在数据库设计时,为某些字段设置了默认值,在修改进行提交以后,数据库会自动修改对应字段的所有行的默认值,从而导致了数据库与数据集中数据的不一致,使ADOQuery无法对数据集进行定位。
2.数据库对应的表没有主键,输入了重复的数据以后,数据库里有两条一样的数据,从而使ADOQuery无法对数据进行定位。
解决方法:
1.修改数据库设计,不再设置默认值,为数据库表定义主键,保证其唯一性。
2.在执行完ADOQuery.Post之后,执行ADOQuery.Refresh,对于设置默认值的情况可以解决。
3.改用Insert into sql语句插入,而不是add--post方式. 但这种方式不更新其他打开该表的query,
所以要requery才行, refresh不起作用.
--------------------------------------------------------------------------
外一篇关于oracle中出现该问题的文章:
分析解决delphi的bug:“无法为更新定位行。一些值可能已在最后一次读取后已更改。”
分析错误出现的情况
1.
分析:添加不报错,修改提交时报错,分析可能是执行UPDATE时报错。
2.
分析:第一次修改不报错,第二次就报错,两次都是执行UPDATE,区别可能就是,第一次和第二次的一些状态不同了。
跟踪ORACLE,得到执行错误时的脚本
UPDATE "测试" SET "档号"=:V00001 WHERE "档号"=:V00002 AND
"ROWID"=:V00003
Bind#0
Bind#1
Bind#2
value="AAAD7CAAGAAACgPAAI"
跟踪ORACLE,得到执行正确时的脚本
UPDATE "测试" SET "档号"=:V00001 WHERE "档号" IS NULL AND
"ROWID"=:V00002
Bind#0
Bind#1
根据以上跟踪的结果,找到了问题的所在,"档号"=’’ 和 "档号" IS NULL 在ORACLE是不同的,
"档号"=’’在ORACLE 不能定位到正确的记录行,所以造成了更新的失败。
我们知道ORACLE中文本型字段有两种状态 NULL 和NOT NULL,而MSSQL中有三种状态 NULL,NOT NULL 和’’
,这就是为何以上程序在MSSQL中运行正常,而在ORACLE中却不能正常的原因了。(注:Oracle中如设置一个文本字段=’’,Oracle会自动将其转换为NULL,但是在查询时如果这样用就不行,不知能不能算是ORACLE的一个BUG。)
为何在第一次编辑时能正确生成代码,而在第二次就不可以了。原因就是Delphi中的TField的属性IsNULL,第一次其从数据库中取出了正确的状态为IsNull=True,编辑之后,如果数值=’’,其IsNull就是False,而TAdoQuery生成更新脚本时就是根据这个属性来生成=’’和
IS NULL这两种脚本的。
找到问题,我们就可以写代码来解决问题了。分析以上问题,我们只要在使用ORACLE数据库时,在提交前,将文本字段=’’字段的IsNull=True,就可以了,我们在TADOQuery的BeforePost事件中添加代码,代码如下:
procedure TFrmData.adoqMainBeforePost(DataSet: TDataSet);
var
begin
end;
还有一种方法:
置ADOQuery控件的CursorLocation属性为clUseServer