2008-10-31 11 views
10

Algunos de mis procedimientos almacenados MS SQL producen mensajes usando el comando 'imprimir'. En mi aplicación Delphi 2007, que se conecta a MS SQL usando TADOConnection, ¿cómo puedo ver el resultado de esos comandos 'imprimir'?Ver el resultado de las instrucciones 'print' usando ADOConnection en Delphi

Requisitos clave: 1) No puedo ejecutar la consulta más de una vez; podría estar actualizando cosas. 2) Necesito ver los resultados de 'impresión' incluso si se devuelven conjuntos de datos.

Respuesta

9

Eso fue muy interesante ...
El evento OnInfoMessage de ADOConnection funciona pero el Demonio está en los detalles!

puntos principales:
uso CursorLocation = clUseServer en lugar de la clUseClient por defecto.
use Open y no ExecProc con su ADOStoredProc.
use NextRecordset del actual para obtener lo siguiente, pero asegúrese de verificar que tenga uno abierto.
use SET NOCOUNT = ON en su procedimiento almacenado.

lado SQL: el procedimiento almacenado

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 

lado Delphi:
Crear un nuevo VCL formularios de solicitud.
Ponga una nota y un botón en su formulario.

copiar el siguiente texto, cambiar el Catálogo y origen de datos y pegarla en su Formulario

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 

En el OnInfoMessage del ADOConnection poner

Memo1.Lines.Add(Error.Description); 

Para el ButtonClick, pega este código

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

Esto definitivamente me puso en el camino correcto: para mayor flexibilidad, utilicé un TADOCommand en lugar de un TADOStoredProc, y todavía funciona. SET NOCOUNT ON también parece ser opcional: solo imprime mensajes extra si no lo tiene.Y clUseServer hace que los conjuntos de registros no se puedan utilizar en una TDBGrid :( – apenwarr

+1

El código OninfoMessage anterior solo muestra el primer mensaje PRINT. Para imprimirlos todos (por ejemplo, si hay más de 1 PRINT declaraciones entre las instrucciones SELECT), haga esto: var i: entero; comienzan para i: = 0 a AdoConnection1.Errors.Count - 1 hacer comienzan // cxMemo1.Lines.Add (Error.Description); cxMemo1.Lines.Add ( ADOConnection1.Errors.Item [i ] .Description); end; end; –

1

No creo que eso sea posible. Puede usar una tabla temporal para volcar las instrucciones de impresión y devolverlas junto con los resultados.

3

En las clases de conexión de .NET hay un evento llamado InfoMessage. En un controlador para este evento, puede recuperar InfoMessage (declaraciones de impresión) desde los argumentos del evento.

Creo que Delphi tiene un evento similar llamado "OnInfoMessage" que podría ayudarlo.

+1

¡Esto está cerca! Funciona si configuro command.ExecuteOptions = [eoExecuteNoRecords]. Pero eso me impide obtener ningún conjunto de datos. Hmm ... – apenwarr

0

Algunas mejoras en el código de Francois (probado con DXE2) para atender para múltiples declaraciones de impresión y los resultados de una cantidad variable de selecciones. Los cambios son sutiles.

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; 
Cuestiones relacionadas