MS SQL ストアド プロシージャの一部は、'print' コマンドを使用してメッセージを生成します。TADOConnection を使用して MS SQL に接続する Delphi 2007 アプリケーションで、これらの「印刷」コマンドの出力を表示するにはどうすればよいですか?
主な要件: 1) クエリを複数回実行できない。物事を更新している可能性があります。2) データセットが返された場合でも、「印刷」結果を確認する必要があります。
MS SQL ストアド プロシージャの一部は、'print' コマンドを使用してメッセージを生成します。TADOConnection を使用して MS SQL に接続する Delphi 2007 アプリケーションで、これらの「印刷」コマンドの出力を表示するにはどうすればよいですか?
主な要件: 1) クエリを複数回実行できない。物事を更新している可能性があります。2) データセットが返された場合でも、「印刷」結果を確認する必要があります。
それは興味深いものでした...
ADOConnection からの OnInfoMessage イベントは機能しますが、悪魔は細部に潜んでいます!
要点:
デフォルトの clUseClient の代わりに CursorLocation = clUseServer を使用します。
ADOStoredProc では ExecProc ではなく Open を使用してください。
現在のものから NextRecordset を使用して次のものを取得しますが、1 つ開いていることを確認してください。
ストアド プロシージャで 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 フォーム アプリケーションを作成します。
フォームにメモとボタンを配置します。
次のテキストをコピーし、カタログとデータ ソースを変更して、フォームに貼り付けます。
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 put の 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;
.net の接続クラスには、InfoMessage というイベントがあります。このイベントのハンドラーでは、イベント引数から InfoMessage (print ステートメント) を取得できます。
Delphi には、「OnInfoMessage」と呼ばれる同様のイベントがあり、それが役立つと思います。
私はそれが可能だとは思わない。一時テーブルを使用して印刷ステートメントをダンプし、結果とともに返すことができます。
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;