Error handling when executing SQL script with multiple INSERT, UPDATE,… statements using ADO in Delphi
在执行包含多个INSERT,UPDATE,...语句的SQL脚本时,我遇到了Delphi / ADO错误处理的困难。只有当脚本的第一个SQL语句失败时,才会在Delphi中出现异常。如果第一个语句通过,则Delphi中不会有任何异常,无论脚本中发生什么。
这是我使用的Delphi代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | var DataSet: TADOQuery; begin ... try DataSet.Close; DataSet.ParamCheck := true; DataSet.SQL.LoadFromFile(FileName); DataSet.Prepared := true; try DataSet.ExecSQL; finally DataSet.Close; end; except on E: Exception do Logging.AddText(E.ClassName + ' error raised when executing ' + FileName + '. Message: ' + E.Message); end; ... end; |
为了测试我使用了这个简单的脚本:
1 2 3 4 5 | INSERT INTO TESTTABLE VALUES ('John', 24); INSERT INTO TESTTABLE VALUES ('Ed', '32'); |
其中TESTTABLE只是一个包含两列的简单表:Name NVARCHAR(50)和Age INT。
例如,当您在第一个INSERT语句中替换24个'twentyfour'并使用Delphi代码运行脚本时,Delphi / ADO将引发异常。但是当你在第二个INSERT语句中替换32,例如'threetwo'时,就不会有异常。
我试着通过将脚本放在存储过程"dbo.ErrorHandling"并发送来解决这个问题
1 | EXEC dbo.ErrorHandling |
ADO,但它没有帮助。
1 2 3 4 5 6 7 8 | CREATE PROCEDURE dbo.ErrorHandling AS BEGIN INSERT INTO TESTTABLE VALUES ('John', 24); INSERT INTO TESTTABLE VALUES ('Ed', '32'); END |
我可以通过在脚本中使用TRY和CATCH来解决问题,并让它将错误记录到LOGGING表中。每次执行脚本后,Delphi都可以检查此表是否存在新错误。
但是,是否有可能在Delphi中捕获所有SQL服务器错误,或者我必须逐个执行INSERTS,UPDATES,......
我使用Delphi XE6和SQLServer 2008 R2
我不会看看AdoConnection的错误集合。
TAdoConnection.Errors
您需要使用ExecuteNoRecords选项配置TADOQuery或TADOCommand:
1 2 | Query := TADOQuery.Create(nil); Query.ExecuteOptions := [eoExecuteNoRecords]; |
它在.NET中是一样的。如果在SqlCommand上使用ExecuteReader()或ExecuteScalar(),则如果单个命令中的第一个语句序列成功,则不会引发异常 - 无论后续语句有多少失败。
但是,如果调用ExecuteNonQuery(),则无论如何都会抛出正确的异常。
我从来没有找到任何方法可以立即可靠地处理多个查询并明智地检测问题。我相信正确的解决方案是一次执行一个语句。
这是我的一个程序中的代码完全按照J__在评论中描述的内容。它在每个语句后使用GO处理SQL Server样式脚本。您可以将"GO"检测器替换为语句结束的其他指示。它将整个事务批量化为单个全有或全无交易。最后一个终于有一些代码将最后一个查询保存到屏幕备忘录中,这样你就可以看到如果有异常则会失败。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | procedure TDupFrame.LoadButtonClick(Sender: TObject); var Query: TADOQuery; Reader: TStreamReader; Line: string; begin Query := TADOQuery.Create(nil); try Query.Connection := ConfModule.ADOConnection; Query.Connection.BeginTrans; if ScriptOpenDialog.Execute(Self.Handle) then begin Reader := TStreamReader.Create(ScriptOpenDialog.FileName); try Query.SQL.BeginUpdate; while not Reader.EndOfStream do begin Line := Reader.ReadLine; if not SameText(Line, 'GO') then begin Query.SQL.Add(Line); end else begin Query.SQL.EndUpdate; Query.ExecSQL; Query.SQL.Clear; Query.SQL.BeginUpdate; end; end; Query.SQL.EndUpdate; if Query.SQL.Count > 0 then Query.ExecSQL; finally Reader.Free; end; Query.Connection.CommitTrans; end; finally SQLMemo.Lines.Assign(Query.SQL); // rollback if we have missed the commit (ie an exception occurred) if Query.Connection.InTransaction then Query.Connection.RollbackTrans; Query.Free; end; end; |