Sql server 执行包含多个INSERT、UPDATE、,。。。Delphi中使用ADO的语句

Sql server 执行包含多个INSERT、UPDATE、,。。。Delphi中使用ADO的语句,sql-server,sql-server-2008,delphi,ado,Sql Server,Sql Server 2008,Delphi,Ado,当执行一个包含多个插入、更新、,。。。陈述只有当脚本的第一条SQL语句失败时,我才能在Delphi中得到一个异常。如果第一条语句通过,那么无论脚本中发生什么,Delphi中都不会出现异常 这是我使用的Delphi代码: var DataSet: TADOQuery; begin ... try DataSet.Close; DataSet.ParamCheck := true; DataSet.SQL.LoadFromFile(FileName);

当执行一个包含多个插入、更新、,。。。陈述只有当脚本的第一条SQL语句失败时,我才能在Delphi中得到一个异常。如果第一条语句通过,那么无论脚本中发生什么,Delphi中都不会出现异常

这是我使用的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只是一个简单的表,包含两列:Name NVARCHAR(50)和Age INT

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

我试图通过将脚本放入存储过程“dbo.ErrorHandling”并发送

EXEC dbo.ErrorHandling
这是胡闹,但没用

CREATE PROCEDURE dbo.ErrorHandling
AS
BEGIN
  INSERT INTO TESTTABLE
  VALUES ('John', 24);
  INSERT INTO TESTTABLE
  VALUES ('Ed', '32');
END
我可以通过在脚本中使用TRY和CATCH来解决这个问题,并让它将错误记录到日志表中。Delphi可以在每次脚本执行后检查此表是否有新错误

但是,是否有可能在Delphi中捕获所有SQL server错误,或者我必须执行插入、更新,。。。一个接一个


我使用DelphiXe6和SQLServer2008R2,我从来没有找到一种方法可以一次可靠地处理多个查询并明智地检测问题。我认为正确的解决方案是一次执行一条语句

这是我的一个程序中的代码,正是J_uu在他的评论中描述的。它处理SQL Server样式的脚本,每个语句后面都有一个GO。您可以将“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;

我不会查看AdoConnection的Errors集合。

您需要使用ExecuteRecords选项配置TADOQuery或TADOCommand:

Query := TADOQuery.Create(nil);    
Query.ExecuteOptions := [eoExecuteNoRecords];
在.NET中也是如此。如果在SqlCommand上使用ExecuteReader()或ExecuteScalar(),则如果单个命令中的一系列语句中的第一个语句成功,则不会引发异常,无论后续有多少语句失败


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

我在为新软件版本升级数据库时会这样做。我总是将整个事务放在一个数据库事务中,所以一旦失败,它就会回滚。如果让SQL Server生成脚本,它会在每个命令后面加上GO。如果您在代码中一行一行地浏览文件,按照您的方式构建查询,那么在清除查询并开始下一个查询之前,先查找这些查询并在“开始”之前执行查询。这些有帮助吗?这是这个问题的唯一答案(IMHO)。当只有第二条SQL语句失败时,AdoConnection.Errors.Count的值为零,这让我感到困惑。您知道连接对象中是否存在错误,以及如何读取该错误吗?谢谢Kanitatlan和@J_uuu。这确实是一个很好的解决办法。但我想知道这是否会使进程放慢太多。在开发过程中,我特意选择立即将脚本发送到数据库,以加快复杂脚本的执行。有经验吗?