/*查资料,发帖子总是没有找到满意的答复,一气之下,晚饭也没有吃
就抱着笔记本写这篇贴子.....今天想找一个正确的解决方法*/
一、问题的前期准备工作以及说明
1、环境:Windows Professional2000(SP3)+Delphi6+ADO2.6+MS SQL Server2000
2、操作类型:Master/Detail;创建主从数据表如下:
/*创建主表*/
   Create table tMaster(
      ncID nchar(20) PRIMARY KEY NONCLUSTERED,
      dOptDate datetime not null default GetDate(),
      cField01 char(10),
      fField02 float not null default(0),
      mField03 money not null default(0)
      nOther   char(10) null
   )
/*创建从表*/
   /*ncId,cDKeyFld 共同作为子表tDetail的主键值*/
   CREATE TABLE tDetail (
ncID nchar(10)  NOT NULL REFERENCES tMaster(ncID),
cDKeyFld char(10) NOT NULL ,
cdField02 char(10) NULL ,
cdField03 char(10) NULL 
    ) ON [PRIMARY]   
3、主从数据表的连接方式:
   创建ADOQuery名称为“ADOQryM”
      ADOQryM.SQL.Text:='select * from tMaster';
   创建ADOQuery名称为“ADOQryD”
      ADOQryD.SQL.Text:='select * from tDetail where ncID=:ncId';      
4、在ADOQryM的AfterSroll事件中写如下代码:
     with ADOQryDetail do
     begin
       Close;
       Paramters.ParamByName('ncID').Value := QryADOM.FieldByName('ncID').AsString;
       Open;
     end;
5、书写添加数据、修改数据、取消操作、保存方法       
//add new
  function AddNew:Boolean;
  begin
    ...
    ADOQryM.Append;
    ....
  end;
//edit current data
  function EditCurr:Boolean;
  begin
    ...   
    ADOQryM.Edit;
    ....
  end;
//cancel updatebatch
  procedure CancelOpt;
  begin
    ...   
    ADOQryD.UpdateBatch(arAll);
    ADOQryM.UpdateBatch(arAll);  
    ...
  end;
 //save current operation
   function Save:Boolean;
   begin
     ....
     
     ADOQryD.UpdateBatch(arAll);
     ADOQryM.UpdateBatch(arAll);
     
     ....
   end; 
6、然后创建界面、添加数据集、数据显示控件、操作button.
完成上面主从数据表的updatabatch模式下的添加、修改、保存操作。 应用Delphi中的ADODataset的批处理(LockType=ltBatchOptimistic)
进行数据操作时,总是有这样的错误提示出现:1、当保存(调用Save方法)添加新数据、或者修改当前数据时,出现下面的错误提示:
“无法为更新行集定位:一些值可能已在读取后改变!”
2、当此时调用CancelOpt;(CancelUpdate)方法时,出现下面的错误提示
"行句柄应用了一个已被删除的行或被标示未删除的行!"对于上面这个错误我一直很头疼,我在处理主从表的时候,因为最终保存时需要检验数据的正确性
也就是在进行操作的时候不因为输入的错误而停止输入,而是把数据的检查方在了调用updateBatch
时。所以我试着把上面的数据表的主键都换成了自动增长的字段(分别添加一个字段autoId int).

解决方案 »

  1.   

    续接上面...结果还是同样的错误。然后参考<<delphi 5.X ADOMTSCOM+高级程序设计>>这本书,上面描述的
    ADODataset的批处理没有提到这样的错误。讲得太简单,无奈继续翻查ADO2.5程序员帮助文档,找到如下内容:/***********************************/
    UpdateBatch 方法/***********************************/      将所有挂起的批更新写入磁盘。语法recordset.UpdateBatch AffectRecords参数AffectRecords   可选。AffectEnum 值,指示 UpdateBatch 方法影响的
    记录数目。说明当在批更新模式下修改 Recordset 对象时,请用 UpdateBatch 方法将在
    Recordset对象中所作的所有更改传送到基本数据库。如果 Recordset 对象支持批更新,在调用 UpdateBatch 方法之前,
    可以把对一个或多个记录所作的多个更改在本地缓存。
    如果调用 UpdateBatch 方法时正在编辑当前记录或添加新记录,
    那么在将批更改传送到提供者之前,ADO 将自动调用 Update 方法保
    存对当前记录所作的所有挂起的更改。批更新应当只与键集游标或静态游标配合使用。注意   如果当前 Recordset 中看不到记录(如没有与过滤器相匹配的记录),
    那么将此参数指定为 adAffectGroup 值将导致错误。如果由于与基本数据冲突(如记录已被另一用户删除)而导致对某个或所
    有记录的更改传送失败,提供者将向 Errors 集合返回警告,并发生运行时错误。
    用 Filter 属性 (adFilterAffectedRecords) 和 Status 属性来定位发生冲突的记录。要取消所有挂起的批更新,请使用 CancelBatch 方法。如果设置了 Unique Table 和 Update Resync 动态属性,
    并且 Recordset 是对多个表执行 JOIN 操作得到的结果,
    那么根据 Update Resync 属性的设置,Resync 方法将在
     UpdateBatch 方法之后隐式地执行。在数据源中进行批更新时,各个更新的顺序不必与在本地
     Recordset 中进行批更新的顺序相同。更新顺序取决于提
     供者。编写相互关联的更新代码时请注意这一点,
     如外键将约束插入或更新。
    /***********************************/ 
     CancelBatch 方法/***********************************/      取消挂起的批更新。语法recordset.CancelBatch AffectRecords参数AffectRecords   可选。AffectEnum 值,指示 CancelBatch 方法影响的记
    录数目。说明在批更新模式下,使用 CancelBatch 方法取消 Recordset 中任何挂起的更新。
    如果 Recordset 处于立即更新模式,调用不带 adAffectCurrent 的CancelBatch
    产生错误。
    如果调用 CancelBatch 时在编辑当前记录或添加新记录,那么 ADO 先调用
    CancelUpdate 方法以取消任何缓存的更改。然后取消 Recordset 中所有挂起的
    更改。
    有可能在 CancelBatch 调用后,特别是在添加新记录的过程中无法确定当前记录。
    因此,在 CancelBatch 调用后应考虑将当前记录位置设置为Recordset 中的一个
    已知位置。例如调用 MoveFirst 方法。如果因与基本数据冲突而导致取消挂起更新的尝试失败(如记录已被其他用户删除)
    ,提供者将向 Errors 集合返回警告但不终止程序执行。
    只有在所有提出请求的记录上都发生冲突时才会发生运行时错误。用 Filter 属性
     (adFilterAffectedRecords) 和 Status 属性来定位发生冲突的记录。
    /****************************/
    然后查找例子如下:
    /****************************/
    UpdateBatch 和 CancelBatch 方法范例 (VB)
    本范例演示 UpdateBatch 和 CancelBatch 方法。Public Sub UpdateBatchX()    Dim rstTitles As ADODB.Recordset
        Dim strCnn As String
        Dim strTitle As String
        Dim strMessage As String    ' Assign connection string to variable.
            strCnn = "Provider=sqloledb;" & _
            "Data Source=srv;Initial Catalog=Pubs;User Id=sa;Password=; "    Set rstTitles = New ADODB.Recordset
        rstTitles.CursorType = adOpenKeyset
        rstTitles.LockType = adLockBatchOptimistic
        rstTitles.Open "Titles", strCnn, , , adCmdTable
        
        rstTitles.MoveFirst    ' Loop through recordset and ask user if she wants 
        ' to change the type for a specified title.
        Do Until rstTitles.EOF
            If Trim(rstTitles!Type) = "psychology" Then
                strTitle = rstTitles!Title
                strMessage = "Title: " & strTitle & vbCr & _
                    "Change type to self help?"            If MsgBox(strMessage, vbYesNo) = vbYes Then
                    rstTitles!Type = "self_help"
                End If
            End If        rstTitles.MoveNext
        Loop    ' Ask the user if she wants to commit to all the 
        ' changes made above.
        If MsgBox("Save all changes?", vbYesNo) = vbYes Then
            rstTitles.UpdateBatch
        Else
            rstTitles.CancelBatch
        End If    ' Print current data in recordset.
        rstTitles.Requery
        rstTitles.MoveFirst
        Do While Not rstTitles.EOF
            Debug.Print rstTitles!Title & " - " & rstTitles!Type
            rstTitles.MoveNext
        Loop    ' Restore original values because this is a demonstration.
        rstTitles.MoveFirst
        Do Until rstTitles.EOF
            If Trim(rstTitles!Type) = "self_help" Then
                rstTitles!Type = "psychology"
            End If
            rstTitles.MoveNext
        Loop
        rstTitles.UpdateBatch    rstTitles.CloseEnd Sub  按照上面叙述的,进行批处理的时候应该跟数据表的字段设定无关,只要指定
    数据表的主键值,保证数据的唯一性updatebatch就应该可以执行的啊?
    请看下面这段话:  ADO的BatchUpdate功能和BDE/IDAPI的CachedUpdate非常类似。它的工作原
    理就是当ADO从数据源取得数据之后,客户端对于所有数据的修改都暂时储存在
    客户端的缓存中,而不是立刻更新回数据源中。而当客户端决定要把所有的修改
    更新回数据源时,才调用A D O的方法,把所有的修改更新回数据源中。
    使用BatchUpdate方法来处理数据的好处是客户端和数据源之间不会产生密切
    的互动,因此可以降低数据源的负荷。此外也可以减少网络的Roundtrip,这在拥
    有大量客户端的应用系统中是非常有帮助的。此外由于BatchUpdate是把客户端对
    于数据的修改暂时储存在客户端内存中,因此它对于数据的修改动作非常快速,只
    在最后把所有的修改更新回数据源时才需要比较多的时间。不过使用BatchUpdate
    也有一些问题,那就是程序员必须撰写较多的程序代码来处理数据更新错误的情形。
    这是因为当客户端在修改数据时,可能已经有其他的用户改变了数据源中的数据,
    因此当客户端把修改的数据更新回数据源时便可能会发生数据冲突或是错误的情
    形。所以当程序员在使用BatchUpdate时,一定要搭配错误处理程序代码,才能够
    撰写出安全坚固的应用程序。在本书稍后的章节中会讨论如何在ADO应用程序中
    处理错误的情形。---摘自 Delphi 5.x ADO/MTS/COM+高级程序设计篇   根据ADO的BatchUpdate的处理原理,像上面得BatchUpdate操作(
    单独用户独占式连接)应该时没问题的,但是为什么频繁发生这样的异常错误呢?
    是ADO驱动程序的问题,还是MS SQL Server的问题?大家帮我看看吧?
    非常感谢!非常感谢!非常感谢!
      

  2.   

    你看一下你数据集的CoursorLocation 是否为clUseClient 如果不行则把它改成 clUseServer
     CoursorType 设为 ctKeyset 或 ctStatic  , LockType 设为 ltBatchOptimistic看看行不行
      

  3.   

    SayForever(恒) :我早就这样做了,可是不行。因为要进行批处理
    这些设置肯定要的。
    Up~~~~~~~~~~~~~~~~~~``
    谢谢大家,帮我Up~~~~~~~~~~~正在等待,捉摸答案~~~~~~~~~~~
      

  4.   

    直接用sql语句
    不要用adodataset
      

  5.   

    批处理操作的错误中,大部分的错误是因为Microsoft Cursor Engine中
    的异常引起的。“键列信息不足或者不正确。更新影响到过多的行”我明明
    修改后的数据在数据表只有一条,并却主键信息足以满足修改的条件,
    但是还是出现这个错误。另外我写了一个小的批处理程序,大家需要看看的话,
    我发给你。希望和高手共同把这个问题解决了。
    同时我在进行批处理操作的时候,设置了默认值也没有问题,但是我增加了一个
    Insert触发器就不行了,老是提示错误信息。大家有兴趣的话,共同探讨。我已经
    搞了一周了。翻了很多东西,但是帮助不大。