FAQ > 金融建模 > 第三方交互 > 数据库

Q:与数据库交互执行报错:连接占线导致另一个hstmt    

简述
报错信息:SQLPost->[FireDAC][Phys][ODBC][Microsoft][ODBC SQL Server Driver]连接占线导致另一个hstmt
  • 相关链接:FAQ:TS-SQL操作及其效率的分析
    问题报错操作代码:

    ret:=update sqltable 'FutureArchive' of 'DBName' set ['IsValid']=0
      where ['TSID']=5881 end ;
      if ret=0 then return sqlerrormsg()
      else return ret;

    经常操作不成功,报如下错误:


    问题分析:
      从该段代码中可以看出,sqltable后面是表名,相当于select *取整个表的数据,处理整张表的游标集,若表格很大的话,效率会很低。
      原因是,本条语句是将整个表格中每一条记录从数据库中复制到客户机,然后用TS-SQL对整表的数据进行where条件定位再做update,完全没有用到数据库的任何SQL。

    造成以下几个问题:
    1、由于TSL-SQL基于DBMS客户机处理整个表,导致整个过程游标量巨大,内存消耗巨大、处理速度极慢。(此时,sqltable会比hugesqltable更大)

    2、造成冲突,原因是操作时其他操作导致游标组变更,变更时导致冲突。
      当利用巨大的游标集在做TS-SQL更新操作时,后台有另外的程序对该表进行了update/delete等操作,在遍历时有可能将需要更新的游标已经做了更改,导致该游标实际已消失,这种情况下,对现有的游标组会出问题,导致报错。
      所以,会有时操作成功,而有时操作报冲突错误。
      报冲突错误原因通俗点可以理解为,由于数据集过大,处理效率慢,导致执行时间长,当期间后台有人修改了表的数据时,那么就会报这个冲突错误。

    解决办法一:用SQL缩小更新范围,优化效率,减少游标占用时间,降低出错率
    例如:将where子语句在SQL层操作,减少游标量

    ret:=update sqltable 'select * from FutureArchive where tsid=5881'
         of 'DBName'
         set ['IsValid']=0 end ;
      if ret=0 then return sqlerrormsg()
      else return ret;


    解决办法二:用EXECSQL交互
    EXECSQL的劣势:由于SQL对字符串的大小以及BLOB等类型无法支撑,TS-SQL是一种便捷的方案。
    EXECSQL的优势:
    完全SQL操作,使用数据库的本身,执行效率高。
    通过参数支持模式,可以将复杂类型数据传递给SQL。


    例如,上面的案例代码可以用以下代码来实现:

    sql:="update FutureArchive set IsValid=0 where tsid=5881";
      ret:=execsql('DBName',sql,data);
      if ret=0 then return sqlerrormsg()
      else return ret;


    与数据库交互方式的建议
    1、做select/update/delete操作时,可尽量用EXECSQL方式交互。
    2、SQL操作比TS-SQL操作效率高,在使用TS-SQL操作数据库时,尽量将where子语句放到SQL中完成,达到减少TS-SQL操作,提高执行效率,降低冲突事件发生的概率的目的。