1. 程式人生 > >SQL存儲過程內部RaisError客戶端抓不住彈不出錯誤的解決

SQL存儲過程內部RaisError客戶端抓不住彈不出錯誤的解決

log ota 會同 atd 測試 cap connected 數據 rap

我們有個海宏商業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.

  

SQL存儲過程內部RaisError客戶端抓不住彈不出錯誤的解決