2008-10-31 8 views
10

Einige meiner gespeicherten MS SQL-Prozeduren erzeugen Nachrichten mit dem Befehl 'print'. Wie kann ich in meiner Anwendung Delphi 2007, die mit TADOConnection eine Verbindung zu MS SQL herstellt, die Ausgabe dieser "print" -Befehle anzeigen?Anzeigen der Ausgabe von 'print' Anweisungen mit ADOConnection in Delphi

Schlüsselanforderungen: 1) Ich kann die Abfrage nicht mehr als einmal ausführen; es könnte Dinge aktualisieren. 2) Ich muss die 'print' Ergebnisse sehen, auch wenn Datensätze zurückgegeben werden.

Antwort

9

Das war ein interessantes ...
Das OnInfoMessage-Ereignis von der ADOConnection funktioniert, aber der Teufel steckt im Detail!

Hauptpunkte:
Verwendung Cursor = clUseServer anstelle des Standard-clUseClient.
Verwenden Sie Open und nicht ExecProc mit Ihrem ADOStoredProc.
Verwenden Sie NextRecordset aus dem aktuellen, um Folgendes zu erhalten, aber stellen Sie sicher, dass Sie eines geöffnet haben.
Verwenden Sie SET NOCOUNT = ON in Ihrer gespeicherten Prozedur.

SQL-Seite: gespeicherten Prozedur

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 Seite:
eine neue VCL-Formularanwendung erstellen.
Setzen Sie ein Memo und eine Schaltfläche in Ihr Formular.

Kopieren Sie den folgenden Text, ändern Sie den Katalog und Datenquelle und fügen Sie ihn in das Formular

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 

Im OnInfoMessage des ADOConnection setzen

Memo1.Lines.Add(Error.Description); 

Für die Button, diesen Code und fügen

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

Das hat mich definitiv auf den richtigen Weg gebracht: Aus Gründen der Flexibilität habe ich einen TADOCommand anstelle eines TADOStoredProc verwendet, und es funktioniert immer noch. Das SET NOCOUNT ON scheint auch optional zu sein: Es druckt nur zusätzliche Nachrichten, wenn Sie es nicht haben.Und clUseServer macht die Recordsets in einem TDBGrid unbrauchbar :( – apenwarr

+1

Der OninfoMessage-Code oben zeigt nur die erste PRINT-Nachricht. Um sie alle zu drucken (zB wenn es mehr als 1 PRINT-Anweisungen zwischen den SELECT-Anweisungen gibt): var integer; für i beginnen: = 0 bis AdoConnection1.Errors.Count - 1 do // cxMemo1.Lines.Add (Error.Description) beginnen; cxMemo1.Lines.Add ( ADOConnection1.Errors.Item [i ]. Beschreibung); Ende; Ende; –

1

Ich denke nicht, dass das möglich ist. Sie können eine temporäre Tabelle verwenden, um Druckanweisungen auszugeben und sie zusammen mit den Ergebnissen zurückzugeben.

3

In den Verbindungsklassen von .net gibt es ein Ereignis namens InfoMessage. In einem Handler für dieses Ereignis können Sie die InfoMessage (Druckanweisungen) aus den Ereignisargumenten abrufen.

Ich glaube, Delphi hat ein ähnliches Ereignis namens "OnInfoMessage", das Ihnen helfen würde.

+1

Dies ist in der Nähe! Es funktioniert, wenn ich command.ExecuteOptions = [eoExecuteNoRecords] setze. Aber das hindert mich daran, irgendwelche Datensätze zu bekommen. Hmm ... – apenwarr

0

Einige Verbesserungen an Francois 'Code (wie mit DXE2 getestet) zu erfüllen für mehrere Druckanweisungen und die Ergebnisse aus einer variablen Anzahl von Auswahlen. Die Änderungen sind subtil.

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;