SQL存儲過程內部RaisError客戶端抓不住彈不出錯誤的解決
阿新 • • 發佈:2018-08-18
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客戶端抓不住彈不出錯誤的解決