• ADO BUG之'无法为更新定位行....' 解决之道


    ADO BUG之'无法为更新定位行....' 解决之道

    Delphi中,如果使用ADOQuery插入数据没有问题之后对数据进行修改保存时,就会遇到无法为更新定位行,一些值可能已在最后一次读取后已更改的问题。 

    原因有这样几种:

    1.
    在数据库设计时,为某些字段设置了默认值,在修改进行提交以后,数据库会自动修改对应字段的所有行的默认值,从而导致了数据库与数据集中数据的不一致,使ADOQuery无法对数据集进行定位。

    2.
    数据库对应的表没有主键,输入了重复的数据以后,数据库里有两条一样的数据,从而使ADOQuery无法对数据进行定位。

    解决方法:

    1.
    修改数据库设计,不再设置默认值,为数据库表定义主键,保证其唯一性。

    2.
    在执行完ADOQuery.Post之后,执行ADOQuery.Refresh,对于设置默认值的情况可以解决。
     (refresh
    dataset中的默认值字段获得了值,跟数据库中一致了)

    3.
    改用Insert into sql语句插入,而不是add--post方式但这种方式不更新其他打开该表的query, 所以要requery才行, refresh不起作用.

    --------------------------------------------------------------------------
    外一篇关于oracle中出现该问题的文章:
    分析解决delphibug无法为更新定位行。一些值可能已在最后一次读取后已更改。

       Delphi
    在使用ADO操作oracle数据库时,经常会出现无法为更新定位行。一些值可能已在最后一次读取后已更改。的错误,一直没有找到好的解决办法,但同样得代码在SQLServer上却没有问题。上回看到了eygle 如何跟踪oracle的文章有所启发,对程序的数据库操作进行了跟踪,找到了问题的所在。


    分析错误出现的情况

    1.   
    在使用DBGrid对表进行编辑时,添加一行数据,再修改新添加行中的原来为空的一个字段(文本型),改变光标到其它行,此时提示错误。

    分析:添加不报错,修改提交时报错,分析可能是执行UPDATE时报错。

    2.   
    对一条数据中文本字段进行第二次修改时提示同样错误。

    分析:第一次修改不报错,第二次就报错,两次都是执行UPDATE,区别可能就是,第一次和第二次的一些状态不同了。


    跟踪ORACLE,得到执行错误时的脚本

    UPDATE "
    测试" SET "档号"=:V00001 WHERE "档号"=:V00002 AND 

    "ROWID"=:V00003

    Bind#0

     value="eee"

    Bind#1



    Bind#2

    value="AAAD7CAAGAAACgPAAI"



    跟踪ORACLE,得到执行正确时的脚本

    UPDATE "
    测试" SET "档号"=:V00001 WHERE "档号" IS NULL AND "ROWID"=:V00002

    Bind#0

     value="eee"

    Bind#1

     value="AAAD7CAAGAAACgPAAI"



    根据以上跟踪的结果,找到了问题的所在,"档号"=’’  "档号" IS NULL ORACLE是不同的, "档号"=’’ORACLE 不能定位到正确的记录行,所以造成了更新的失败。

    我们知道ORACLE中文本型字段有两种状态 NULL NOT NULL,而MSSQL中有三种状态 NULLNOT NULL ’’ ,这就是为何以上程序在MSSQL中运行正常,而在ORACLE中却不能正常的原因了。(注:Oracle中如设置一个文本字段=’’Oracle会自动将其转换为NULL,但是在查询时如果这样用就不行,不知能不能算是ORACLE的一个BUG)

    为何在第一次编辑时能正确生成代码,而在第二次就不可以了。原因就是Delphi中的TField的属性IsNULL,第一次其从数据库中取出了正确的状态为IsNull=True,编辑之后,如果数值=’’,其IsNull就是False,而TAdoQuery生成更新脚本时就是根据这个属性来生成=’’ IS NULL这两种脚本的。

    找到问题,我们就可以写代码来解决问题了。分析以上问题,我们只要在使用ORACLE数据库时,在提交前,将文本字段=’’字段的IsNull=True,就可以了,我们在TADOQueryBeforePost事件中添加代码,代码如下:

    procedure TFrmData.adoqMainBeforePost(DataSet: TDataSet);

    var

     i: Integer;

    begin

     if DATA = 'ORACLE' then //
    自定义,标明数据库类型

      for i := 0 to DataSet.FieldCount - 1 do

      begin

       if DataSet.Fields[i].IsNull then

        exit;

       if DataSet.Fields[i].DataType in [ftString
     ftFixedChar ftGuid

        ftWideString] then

        if DataSet.Fields[i].AsString = '' then

         DataSet.Fields[i].Clear;

      end;

    end;

  • 相关阅读:
    【c++】中文设置
    《谁动了我的奶酪》读后感
    KMP算法的C++实现
    我也说说中文分词(上:基于字符串匹配)
    删除字符串中的空格
    linux jdk bin安装
    笔试题汇总
    栈的压入、弹出序列
    顺序打印矩阵
    二叉树镜像
  • 原文地址:https://www.cnblogs.com/karkash/p/3036111.html
Copyright © 2020-2023  润新知