很古老的问题——Default,Trigger,sp修改数据 BY CJ(200分)

  • 主题发起人 主题发起人 CJ
  • 开始时间 开始时间
C

CJ

Unregistered / Unconfirmed
GUEST, unregistred user!

一. 问题提出:
做一个程序,用adotable/adoquery连接sql server,为了简单期间,DBGrid+DBNavigator。
服务器上随便用个数据表,其中一个字段设立Default值,假定为列A。运行程序后,新建记录,
填写其它必须记录,列A值为空,保存之。此刻,系统报错:服务器无法定位记录,
可能记录被修改,或者是记录被删除。

二. 问题分析:
由于Default值有系统自动赋予,客户端无法得知,所以造成无法定位刚才的记录。
同样的,如果用Insert Trigger修改数据,或后台用sp修改数据,也会造成类似问题。

三. 可能的解决方法:
1. 在客户端给出和服务器端一样的Default定义,这也是我一直用的方法,
以下给出实例〔伪?〕代码:
...
const
TB1_NAME_FEFAULT = 'NO NAME';
...
procedure ADOTable1BeforePost(...);
begin
with ADOTable1 do
begin
if FieldByName('Name').isNil then //如果为空
FieldByName('Name').Value := TB1_NAME_FEFAULT; //赋予默认值
if ...
...
FieldByName('Age').Value := GetAge(FieldByName('Age').Value,now);
//GetAge为自定义函数,计算年龄,本来可以有Trigger完成
...
end;
end;
同样的,需要用任何trigger计算的地方也在这里一并计算。见以上代码。
但是问题是,如此一来,服务器端的Trigger、Defualt变的毫无用处。而且,在多个客户端
应用程序共同实用服务器端数据库的情况下,每次服务器端的修改,你必须重新发布应用程序。
三层在这个问题上表现良好,任何规则有中间层负责,可两层怎么办?真的无法解决了吗?
曾经想过直接读取服务器端信息,把Default找出来,事先填在客户数据上。可这样也太夸张了
吧?何况Trigger的问题还没有解决。
 
不用数据感知控件UPDATE/INSERT/DELETE什么事都没了.
 
写OnPostError事件,设置Action为daAbort,
在AfterPost事件refresh dataset
如何?
 
BDE通过Table等DataAccess组件保存一条记录后,
总是会再查一下刚刚的记录是不是真的写到数据库里了。
这时发送的SQL语句的Where 子句因组件的UPdateMode而异,
如果是upWhereAll则包含所有子段,upWhereKeyOnly则只包含主键子段
如果返回的结果不等于1,BDE就会报一个错误:服务器无法定位记录

了解了原因,我想你一定能找出答案了。

另外,建议在调试Delphi的数据库错误时,一定要结合数据库的Trace工具来做。
比如SQL Server的Profile。有时,程序报错不是SQL写错了,也不是代码错了,
也不是Delphi有Bug,而是Delphi不许你这样做。

 
我倒认为用个INSERT SQL,什么事也没有。
 
to 吴:不错,不过,现在我要开发效率,呵呵……效率低点,bug多点,代码破烂点都没问题,开发效率高啊。
to add:知其然,必先知其所以然,不错,高。不过我修正一个小小的错误:
>如果是upWhereAll则包含所有子段,upWhereKeyOnly<b><u>则只包含主键子段</b></u>如果返回的结果不等于1,BDE就会报一个错误:服务器无法定位记录
这里错了,如果是upWhereKeyOnly是包括key field和所有被修改过的字段,实际上,这只对update有意义,而非insert
唉,那么一修正,你应该知道,我还是不知道该怎么作了吧?
 
pipi:老早用过,不行!
 
你把cursorlocation改为clUseServer,我想问题应该就解决了,
原因也不用我说了吧。

ps:你所描述的现象似乎都不准确,我没有碰到过连“第一次”保存都不成功的情况,
道理很简单,那时候数据库中没有纪录,因此保存根本不需要定位“原纪录”,
怎么会出现这种错误呢?因此我怀疑你是不是没有升级SP1,ADOsp之类的?

一切讨论首先以升级SP1+ADOsp后为基础,in case...
 
CJ:
我试你说的那中情况,好象没什么错误发生嘛,能更详细些吗?
 
温柔:第一次到可能成功的,可以后就没戏了。
沈:不会吧,我一直都有这个问题,现在我根本不敢用Default和InsertTrigger。


另:能update的我都做了。不过装了以后,这个问题到是还没试验过#@$!$
 
>>温柔:第一次到可能成功的,可以后就没戏了。

问题恰恰在这里,由于你使用Client side cursor,
第一次成功以后,字段有了default值,因此以后再修改,就发生找不到记录的问题。
你换成clUserServer看了么?肯定解决问题的,还不明白?

这么简单的问题...
 
呵呵,我完全按你的条件测试的,还是没有出现。也许是我错误理解了你的意思。
不过我猜,你不是在问题出现后马上问这个问题,可能你搞忘了。 :)
 
TO CJ:
抱歉,我没说清楚。

BDE通过Table等DataAccess组件保存一条记录后,
总是会再查一下刚刚的记录是不是真的写到数据库里了。
这时发送的SQL语句的Where 子句因组件的UPdateMode而异,
如果是upWhereAll则包含所有子段,upWhereKeyOnly则只包含主键子段
如果返回的结果不等于1,BDE就会报一个错误:服务器无法定位记录
====================
应为“如果返回的结果集的记录数不为1”,这里讲UpdateMode是指当控件的
AutoRefresh为真时,Delphi在保存数据后(包括Insert)向数据库回查时,
updatemode会导致Select语句的Where子句的不同。

上面说的情况常见于表具有Identity列。假设这样一个表,
(
ID Int Identity(1,1) not null,
Name varchar2(10) null
)

Delphi用DBGrid+DBNav操作,第一行Name输'Addie',保存。然后第二行Name仍输
'Addie',保存时必报错。原因如上。

另外,一个最重要的错误是我答非所问,你问的是ADO,我说的是BDE。很抱歉。
而你所谈的现象,我实在是测试不出来。
 
TO add:无所谓,讨论讨论也好,不过我要指出你的一个错误:无论update mode 为 what
理论上说(Borland xxx 资料)select 语句永远包含主建;

to 沈:的确,是前年的问题咯,只是最近很少提问了,想提个问题出来哦,当初膏了我好几天,还没空试。

to温柔:thanx, and see,I'll try if 我 am free,分数 you 一定大大地
 
faint!! 日本快板儿? :-)

分数是次要地,解决问题是关键地,不然大富翁的水多多地...
不是急需解决的问题,你应该提的时候说清楚

(怎么这种事情总让我碰上...)
 
NND 7456
现在竟然怎么也测不出那样的问题了!唉,也许是那个ADO PATCH的关系吧……算了,分!
 
不同意,你只要用cluseclient,新增之后再修改,肯定还出问题
而用cluseserver就不会
 
后退
顶部