10

我的一些 MS SQL 存储过程使用“打印”命令生成消息。在我的 Delphi 2007 应用程序中,它使用 TADOConnection 连接到 MS SQL,我如何查看这些“打印”命令的输出?

关键要求:1)我不能多次运行查询;它可能正在更新东西。2) 即使返回数据集,我也需要查看“打印”结果。

4

4 回答 4

9

那是一个有趣的事件...
来自 ADOConnection 的 OnInfoMessage 事件有效,但魔鬼在细节中!

要点:
使用CursorLocation = clUseServer 而不是默认的clUseClient。
在您的 ADOStoredProc 中使用 Open 而不是 ExecProc。
使用当前的 NextRecordset 获取以下内容,但请务必检查您是否打开了一个。
在存储过程中使用 SET NOCOUNT = ON。

SQL 端:你的存储过程

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[FG_TEST]') AND type in (N'P', N'PC'))
  DROP PROCEDURE [dbo].[FG_TEST]
GO
-- =============================================
-- Author:      François
-- Description: test multi ADO with info
-- =============================================
CREATE PROCEDURE FG_TEST
AS
BEGIN
    -- SET NOCOUNT ON absolutely NEEDED
    SET NOCOUNT ON;

    PRINT '*** start ***'

    SELECT 'one' as Set1Field1

    PRINT '*** done once ***'

    SELECT 'two' as Set2Field2

    PRINT '*** done again ***'

    SELECT 'three' as Set3Field3

    PRINT '***finish ***'
END
GO

Delphi方面:
新建一个VCL Forms Application。
在您的表单中放置一个备忘录和一个按钮。

复制以下文本,更改目录和数据源并将其粘贴到您的表单上

object ADOConnection1: TADOConnection
  ConnectionString = 
    'Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security In' +
    'fo=False;Initial Catalog=xxxYOURxxxDBxxx;Data Source=xxxYOURxxxSERVERxxx'
  CursorLocation = clUseServer
  LoginPrompt = False
  Provider = 'SQLOLEDB.1'
  OnInfoMessage = ADOConnection1InfoMessage
  Left = 24
  Top = 216
end
object ADOStoredProc1: TADOStoredProc
  Connection = ADOConnection1
  CursorLocation = clUseServer
  ProcedureName = 'FG_TEST;1'
  Parameters = <>
  Left = 24
  Top = 264
end

在 ADOConnection 的 OnInfoMessage 中放

Memo1.Lines.Add(Error.Description);

对于 ButtonClick,粘贴此代码

procedure TForm1.Button1Click(Sender: TObject);
const
  adStateOpen = $00000001; // or defined in ADOInt
var
  I: Integer;
  ARecordSet: _Recordset;
begin
  Memo1.Lines.Add('==========================');

  ADOStoredProc1.Open; // not ExecProc !!!!!

  ARecordSet := ADOStoredProc1.Recordset;
  while Assigned(ARecordSet) do
  begin
    // do whatever with current RecordSet
    while not ADOStoredProc1.Eof do
    begin
      Memo1.Lines.Add(ADOStoredProc1.Fields[0].FieldName + ': ' + ADOStoredProc1.Fields[0].Value);
      ADOStoredProc1.Next;
    end;
    // switch to subsequent RecordSet if any
    ARecordSet := ADOStoredProc1.NextRecordset(I);
    if Assigned(ARecordSet) and ((ARecordSet.State and adStateOpen) <> 0) then
      ADOStoredProc1.Recordset := ARecordSet
    else
      Break;
  end;

  ADOStoredProc1.Close;
end;
于 2008-11-01T00:27:54.427 回答
3

在.net 的连接类中有一个名为InfoMessage 的事件。在此事件的处理程序中,您可以从事件参数中检索 InfoMessage(打印语句)。

我相信 Delphi 有一个名为“OnInfoMessage”的类似事件可以帮助您。

于 2008-10-31T18:58:07.587 回答
1

我不认为这是可能的。您可以使用临时表转储打印语句并将其与结果一起返回。

于 2008-10-31T18:48:27.843 回答
0

对 Francois 的代码(使用 DXE2 测试)进行了一些增强,以适应多个打印语句和来自可变数量选择的结果。变化是微妙的。

procedure TForm1.ADOConnection1InfoMessage(Connection: TADOConnection;
  const Error: Error; var EventStatus: TEventStatus);
var
  i: integer;
begin
  // show ALL print statements
  for i := 0 to AdoConnection1.Errors.Count - 1 do
  begin
    // was: cxMemo1.Lines.Add(Error.Description);
    cxMemo1.Lines.Add(
      ADOConnection1.Errors.Item[i].Description);
  end;
end;

procedure TForm1.cxButton1Click(Sender: TObject);
const
  adStateOpen = $00000001; // or uses ADOInt
var
  records: Integer;
  ARecordSet: _RecordSet;
begin
  cxMemo1.Lines.Add('==========================');

  ADOStoredProc1.Open;

  try
    ARecordSet := ADOStoredProc1.RecordSet; // initial fetch
    while Assigned(ARecordSet) do
    begin
      // assign the recordset to a DataSets recordset to traverse
      AdoDataSet1.Recordset := ARecordSet;
      // do whatever with current ARecordSet
      while not ADODataSet1.eof do
      begin
        cxMemo1.Lines.Add(ADODataSet1.Fields[0].FieldName + 
          ': ' + ADODataSet1.Fields[0].Value);
        AdoDataSet1.Next;
      end;
      // fetch next recordset if there is one
      ARecordSet := ADOStoredProc1.NextRecordSet(records);
      if Assigned(ARecordSet) and ((ARecordSet.State and adStateOpen) <> 0) then
        ADOStoredProc1.Recordset := ARecordSet
      else
        Break;
    end;
  finally
    ADOStoredProc1.Close;
  end;

end;
于 2012-10-30T18:35:05.997 回答