2014-11-03 330 views
1

执行包含多个INSERT,UPDATE,...语句的SQL脚本时遇到Delphi/ADO错误处理困难。只有脚本的第一条SQL语句失败时,我才会在Delphi中遇到异常。如果第一条语句通过,那么Delphi中不会有例外,无论脚本中发生了什么。在Delphi中使用多个INSERT,UPDATE,...语句执行SQL脚本时的错误处理

这在Delphi代码使用:

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; 

为了测试我用这个简单的脚本:

INSERT INTO TESTTABLE 
VALUES ('John', 24); 

INSERT INTO TESTTABLE 
VALUES ('Ed', '32'); 

其中TestTable的仅仅是含有两列的简单表:名称NVARCHAR(50)和年龄INT。

当您在第一个INSERT语句中用'twentyfour'替换24,并用Delphi代码运行脚本时,Delphi/ADO将引发异常。但是,如果在第二个INSERT语句中将32替换为“thirtytwo”,则不会有例外。

我试图把脚本在存储过程中“dbo.ErrorHandling”和发送

EXEC dbo.ErrorHandling 

到ADO解决这个问题,但它并没有帮助。

CREATE PROCEDURE dbo.ErrorHandling 
AS 
BEGIN 
    INSERT INTO TESTTABLE 
    VALUES ('John', 24); 
    INSERT INTO TESTTABLE 
    VALUES ('Ed', '32'); 
END 

我可以解决通过使用脚本try和catch,并让它记录错误的记录表的问题。在每个脚本执行后,Delphi可以检查这个表是否有新的错误。

但是,是否有可能在Delphi中捕获所有SQL服务器错误,还是必须一个一个地执行INSERTS,UPDATES ......?

我用Delphi XE6和SQLServer 2008 R2

+0

我在为新软件版本升级数据库时执行此操作。我总是把整个事情放在数据库事务中,所以它在失败时回滚。如果让SQL Server生成脚本,它会在每个命令之后放置GO。如果您逐行浏览代码中的文件,按照您的要求构建查询,请在清除查询并开始下一个查询之前查找这些查询并在“GO”之前执行查询。这有帮助吗? – 2014-11-03 15:22:36

回答

0

我从来没有发现的一次和理智发现问题处理可靠几个查询任何方式。我相信正确的解决方案是一次执行一条语句。

这是我的一个程序的代码,完全按照J__在他的评论中描述的内容进行。它在每个语句之后使用GO处理SQL Server样式脚本。您可以用声明结尾的其他指示代替“GO”探测器。它将整个事件分批处理为单一的全或无事务。最后,最后有一些代码将最后一个查询保存到屏幕上的备忘录中,以便在出现异常时查看失败。

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; 
+0

感谢Kanitatlan和@J__。这肯定是一个很好的解决方法。但我不知道这是不是放慢了这个过程。在开发过程中,我故意选择将脚本一次发送到数据库,以加快执行复杂的脚本。任何经验? – Johan 2014-11-07 10:49:46

1

我不会看看AdoConnection的Errors集合。 TAdoConnection.Errors

+1

这个问题的唯一答案(恕我直言)。 – bummi 2014-11-03 22:50:59

+0

让我感到困惑的是,当只有第二个SQL语句失败时,AdoConnection.Errors.Count会给出零。你知道错误是否出现在Connection对象的某个地方,以及它如何被读取? – Johan 2014-11-07 10:19:02

0

您需要配置TADOQuery或TADOCommand与ExecuteNoRecords选项:

Query := TADOQuery.Create(nil);  
Query.ExecuteOptions := [eoExecuteNoRecords]; 

它在.NET一样。如果在SqlCommand上使用ExecuteReader()或ExecuteScalar(),如果单个命令中的第一个语句成功执行,则不会抛出异常 - 无论后续语句失败多少次。

但是,如果调用ExecuteNonQuery(),则会在任何情况下引发适当的异常。

相关问题