SQL存储过程内部RaisError客户端抓不住弹不出错误的解决

我们有个海宏商业erp3,库存部分是用存储过程写的,减库存时会先检查负库存,比如还有5个你想出库6个,存储过程就raisError(‘库存不足‘,16,1)。

最近这一个版本发布后,有客户反映有时候会出负库存。

再一个,我们软件特殊情况下会同时操作本地和远程两个数据库、开两个sql事务,容易产生莫名其妙的错误。

倒腾了一阵,结果汇总在这里,百度上搜不到答案,希望以后有人遇到能管用。

{*****************************************测试目的******************************
sql存储过程中会先检查库存数量,如果库存是负数就raisError(‘库存不足‘,16,1),
这时候发现客户端会截获不住这个错误。
经过测试发现:
  1:用AdoQuery.Open比较保险。能抓做存储过程内部raisEror的错误,直接就报错了。
     用用AdoQuery.ExecSql、AdoStoredProc.ExecProc、Connection.Execute都抓不住错误,
  2:这些方式都能取到存储过程的return值,那么写存储过程时,得在raiseError之后,
     马上return一个错误代码,原则上return 0表示没错误,其他非零值都是错误代码.
     这样程序可以取到这个值,判断这个值是否=0,非0就是有错误
}
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons, DB, ADODB, Grids, DBGrids;

type
  TForm1 = class(TForm)
    btn_ByQuery: TBitBtn;
    btn_ByProc: TBitBtn;
    btn_ByConn: TBitBtn;
    conn_main: TADOConnection;
    qry_main: TADOQuery;
    asp_R: TADOStoredProc;
    Label1: TLabel;
    txt_SQL: TMemo;
    qry_R: TADOQuery;
    Label2: TLabel;
    lbl_Total: TLabel;
    Label3: TLabel;
    ds_main: TDataSource;
    grd_main: TDBGrid;
    Label4: TLabel;
    txt_info: TMemo;
    cbx_execSQL: TComboBox;
    procedure btn_ByQueryClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure btn_ByProcClick(Sender: TObject);
    procedure btn_ByConnClick(Sender: TObject);
  private
    { Private declarations }
  public
    //读取结果
    function showResult:integer;
    //
    procedure showInfo(sInfo:string=‘‘; lTime:boolean=true);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.btn_ByProcClick(Sender: TObject);
var asp:TAdoStoredProc;               n:integer;
    s:String;
begin
    asp:=asp_R;
    with asp do
    try
        close;
        connection.beginTrans;
        //执行
        asp.ProcedureName:=‘testError‘;
        asp.Parameters.Clear;
        asp.Parameters.CreateParameter(‘@RETURN_VALUE‘, ftInteger, pdReturnValue, 10, fgUnAssigned);
        asp.parameters.CreateParameter(‘@sComment‘, ftString, pdInput, 254, ‘AdoStoredProc‘);
        asp.ExecProc;
        n:=round( asp.Parameters.ParamValues[‘@RETURN_VALUE‘] );
        //***************************AdoStoredProc测试结果********************//
        //经过分析发现,在存储过程中raiseError用AdoStoredProc抓不住,只能用return 0的返回值做判断
        If n=0 then showInfo(‘AdoStoredProc执行成功‘)
          else begin
            if connection.errors.count>0 then s:=#13+connection.Errors[0].Description else s:=‘‘;
            Raise Exception.Create(‘AdoStoredProc出错!错误代码:‘+Inttostr(n)+s);
          end;
        //提交事务
        connection.CommitTrans;
    except
        on x:exception do begin
            if connection.InTransaction then connection.RollbackTrans;
            showInfo(x.message);
        end;
    end;
    showResult;
end;

procedure TForm1.btn_ByQueryClick(Sender: TObject);
var qry:TAdoQuery;                  l,lExec,lOpen:boolean;
begin
    qry:=qry_R;                     lExec:=cbx_execSql.itemIndex=0;
    lOpen:=not lExec;
    with qry do
    try
        close;
        connection.beginTrans;
        //执行
        sql.text:=‘declare @n int, @n2 int ‘;
        sql.add(‘  exec @n=testError ‘+quotedStr(‘AdoQuery-‘+cbx_execSQl.text)+‘ ‘);
        if lOpen then sql.add(‘  select @n as vResult ‘); //打开
        //*************关键点:execSQL不会导致报错,而open会导致报错**********//
        if lExec then
            execSQL                 //抓不住存储过程中raiseError
        else
            open;                   //打开能抓住raisError
        if not isEmpty then showInfo(‘AdoQuery执行成功,返回值:‘+fields[0].asString) else showInfo(‘执行完毕,无返回值‘);
        //提交事务
        connection.CommitTrans;
    except
        on x:exception do begin
            if connection.InTransaction then connection.RollbackTrans;
            showInfo(x.message);
        end;
    end;
    showResult;
end;
//用connection执行
procedure TForm1.btn_ByConnClick(Sender: TObject);
var rec:_Recordset;                       conn:TAdoConnection;
    s:String;                             n, n2, nR:integer;
begin
    conn:=conn_main;                      nR:=-1;
    rec:=nil;
    with conn do
    try
        if not conn.Connected then conn.Open;
        conn.BeginTrans;
        //
        with qry_R do begin
            sql.text:=‘declare @n int, @n2 int ‘;
            sql.add(‘  exec @n=testError ‘‘Connection‘‘ ‘);
            sql.add(‘  select @n as vResult ‘);
            s:=sql.text;
        end;

        //*****************用最底层的连接执行兼容sql2000、2008****************//
        //测试发现:存储过程raisError时connection是抓不住的,只能用return值判断
        //用rec.fields[0].value取返回值容易出莫名其妙的错误,还需要继续找可靠的办法
        //nR:=connection.Execute(s)(0);
        //rec:=conn.Execute(s, n2, eoAsyncFetch);
        rec:=conn.Execute(s);                 //, cmdText, [eoAsyncFetch]
        //if (assigned(rec)) and (not rec.EOF) then nR:=rec.Fields[0].Value;
        if nR<>0 then showInfo(‘  Connection出错,结果返回值:‘+intToStr(nR))
          else showInfo(‘Connection执行成功!‘);

        //提交
        conn.CommitTrans;
    except
        on x:exception do begin
            if conn.InTransaction then conn.RollbackTrans;
            showInfo(x.message);
        end;
    end;
    showResult;
end;

//读取结果
function TForm1.showResult:integer;
var qry:TAdoQuery;                          i:integer;
begin
    result:=-1;
    qry:=TAdoQuery.create(self);
    qry.connection:=qry_main.connection;
    with qry do
    try
        qry_main.disableControls;
        //
        close;
        sql.text:=‘select count(1) from employee ‘;
        open;
        if not isEmpty then lbl_total.caption:=intToStr(fields[0].value);
        //表格
        with qry_main do begin
            close;
            sql.text:=‘select top 10 * from employee order by nID desc ‘;
            open;
            for i:=0 to fieldCount-1 do fields[i].DisplayWidth:=14;
        end;
    except
        on x:exception do showMessage(x.message);
    end;
    qry_main.enableControls;
    if assigned(qry) then freeAndNil(qry);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
    try
        lbl_total.caption:=‘‘;
        //
        conn_main.open;
        showResult;
    except
        on x:exception do showMessage(x.message);
    end;
end;

procedure TForm1.showInfo(sInfo:string=‘‘; lTime:boolean=true);
begin
    txt_info.Lines.Add(formatDateTime(‘yyyy-MM-dd HH:mm:ss‘,now)+‘ ‘+sInfo);
end;

end.

  

原文地址:https://www.cnblogs.com/HaiHong/p/9498420.html

时间: 2024-07-30 01:29:01

SQL存储过程内部RaisError客户端抓不住弹不出错误的解决的相关文章

ORA-06502: PL/SQL: numeric or value error: character to number conversion error 错误的解决方法

故障描述:中间层调用存储过程报错,到PL/SQL中对存储过程进行调试报以下错误 ORA-06502: PL/SQL: numeric or value error: character to number conversion errorORA-06512: at "PROCESS_DRUG_SAME_AS_INFO", line 24ORA-06512: at line 1 错误原因: 找到存储过程PROCESS_DRUG_SAME_AS_INFO,并将代码定位在24行,发现代码中两

&quot;客户端无法连接到远程计算机&quot;错误的解决方法

问题: 客户端无法连接到远程计算机. 可能没有启用远程连接或者计算机太忙不能接受新的连接. 也可能是网络问题阻止连接.请稍后重新尝试连接. 如果问题仍然存在 请与管理员联系. 解决方法: 1.首先确认远程登陆的服务是打开的,也就是Remote Desktop Services(Windows xp里面是Terminal Services)服务:右键我的电脑--管理--服务,找到Terminal Services服务,启动这个服务即可 2.右键我的电脑--属性--远程,勾选“允许用户远程登陆到服务

Win7 64位 + LoadRunner 11录制时弹不出IE的解决办法 Win7 64位 + LoadRunner 11录制时弹不出IE的解决办法

Win7 64位 + LoadRunner 11录制时弹不出IE的解决办法 Win7 64位 + LoadRunner 11录制时弹不出IE的解决办法 1. 卸载IE9( 装了Win7 64位后,默认的IE是IE9,先把IE9卸载. 打开控制面板->程序->查看已经安装的更新,找到IE9卸载,重启. 卸载后,IE自动变为IE8. 其中存在C:\Program Files\Internet Explorer下面的是64位的IE, 存在C:\Program Files(x86)\Internet

Loadrunner | 录制脚本时弹不出IE的解决办法

Loadrunner在录制脚本的时候有时候会遇到弹不出IE的问题,那怎么解决呢?别急,按照以下几个步骤操作,一般就可以解决这个问题. 1. IE浏览器取消勾选[启用第三方浏览器扩展] 启动IE,从[工具]进入[Internet选项],切到高级,去掉[启用第三方浏览器扩展(需要重启动)]的勾选,然后再次运行VuGen即可.通常安装Firfox等浏览器后,都会 勾选这个选项,导致不能正常录制.因此建议LoadRunner的相关主机上保持一个干净的测试环境. 2. 将IE浏览器退回到低版本 如果你的环

记32位Oracle客户端登录报12560协议适配器错误的解决办法

国庆买了一台新电脑ThinkPad E431,i5双核CPU,8G内存,硬盘比较坑爹5400转的500G,重点是预装win8的64位简体中文版.大学时买了第一台电脑神舟笔记本,因为神舟电脑便宜,所以没有预装正版系统:工作了以后给老婆买了一台电脑惠普笔记本,当时一个朋友开店做惠普电脑代理,为了帮他开个张,就从他那里买的,他给装了个盗版winxp.其实心里还是很支持正版windows操作系统的,而且新公司给发的工作电脑又很破,严重影响工作心情,所以就买了联想的ThinkPad,于是乎就有了正版win

phpstudy开启php_intl扩展启动弹出错误的解决方法

最近因为要使用YII2框架开发,发现有些模块需要支持php_intl国际化支持拓展,本地搭建了phpstudy集成环境,开启php_intl扩展后,任何版本启动时都弹出phpstartup,错误都没显示 当时觉得很奇怪,通过一系列的查找,发现php_intl需要依赖于icu库的,索性照着方法做下去( 下载icu库 -----放到apache\bin\).目录不一定是apache\bin\ 取决于你环境目录,windows key + PAUSE --系统- -高级--环境变量查看你的环境的全局路

SQL 存储过程入门(事务)(四)

SQL 存储过程入门(事务)(四) 本篇我们来讲一下事务处理技术. 为什么要使用事务呢,事务有什么用呢,举个例子. 假设我们现在有个业务,当做成功某件事情的时候要向2张表中插入数据,A表,B表,我们插入的顺序是先插入A,再插入B表,如果都顺利插入成功了,当然没有问题,如果任意一张表插入失败了,而另一张表插入成功了,插入成功的表就是垃圾数据了.我们要判断,任意一张表插入失败都回滚,就是都不插入,这就是事务的基本使用. 一,sql事务定义 所谓事务是用户定义的一个数据库操作序列,是一个不可分割的工作

SQL Server 中 RAISERROR 的用法(转)

在存储过程中进程会处理一些逻辑性的错误,如:将RMB转换为USD时,没有查询到想要的汇率 这个时候最好在存储过程中抛个异常,方便自己查找错误信息... 其语法如下: RAISERROR ( { msg_id | msg_str | @local_variable }                    { ,severity ,state }                    [ ,argument [ ,...n ] ]           )          [ WITH optio

SQL Server 中 RAISERROR 的用法

raiserror  是由单词 raise error 组成     raise  增加; 提高; 提升 raiserror 的作用: raiserror 是用于抛出一个错误.[ 以下资料来源于sql server 2005的帮助 ] 其语法如下: RAISERROR ( { msg_id | msg_str | @local_variable } { ,severity ,state } [ ,argument [ ,...n ] ] ) [ WITH option [ ,...n ] ] 简