SSMSなどの個別の行数を取得するにはどうすればよいですか?


8

InfoMessageイベントでとエラー出力ExectueNonQueryをキャッチすることPRINTを含め、を介してストアドプロシージャを実行しているクライアントc#プログラムがあります。正常に動作しますが、変なことに気づきました。

SSMSからストアドプロシージャを実行すると、[メッセージ]タブで実行された個々のSQLステートメントの行数が表示されます(InfoMessageから送信されたかのように)。ただし、他の同じ出力はすべてキャッチしますが、プログラムはこれらのメッセージを表示しません。代わりに、ExecuteNonQuery関数の結果で影響を受ける行を返します。これは、個々の行カウントのすべての合計です(これは一種の役に立たないものです)。

たとえば、この手順は次のとおりです。

use [tempdb]
go

SELECT  * 
INTO    MyCols
FROM    sys.columns
go

CREATE PROC foo As

    UPDATE  MyCols
    SET     name = name + N''
-- SSMS shows (662 row(s) affected)

    UPDATE  MyCols
    SET     name = name + N''
    WHERE   name like '%x%'
-- SSMS shows (59 row(s) affected)

PRINT 'bar'
-- both SSMS and ExecuteNonQuery get this

-- ExecuteNonQuery returns 721 rows affected
GO

場合fooprocが実行され、SSMSは、662と59のrowcountsを表示するが、ExecuteNonQuery唯一の721の合計を返します。

では、どうすればSSMSが取得しているのと同じ情報を取得できますか?


ここで明確にするためにPRINT @@ROWCOUNT、すべてのSQLステートメントの後にs を追加するようにストアドプロシージャを変更する方法には興味がありません。私はそれを行う方法を知っています、そしてそれは様々な理由でほとんどの場合オプションではありません。

ここでSSMSが実行していることを実行する方法を尋ねています。この時点で(今のところ、とりあえず)必要なすべてのクライアントコードを変更できます。正しく変更したいと思います。

回答:


6

SqlCommand.StatementCompletedイベントは、バッチ内の各ステートメントの後に起動します、そしてイベントの特性の一つは(まあ、ほとんど唯一のプロパティ)は、イベントを発射した文によって影響を受ける行の数です。

いくつかのメモ:

  • この情報を得るための要件は、あなたがなかったことではない指定SET NOCOUNT ON;、または逆に、あなたがやった指定しますSET NOCOUNT OFF;
  • すべてのイベントExecute___()は、実行中ではなく、各の完了時に発生します。
  • StatementCompletedEventArgs.RecordCount行カウント含むSELECT一方、文をSqlDataReader.RecordsAffectedプロパティのみDMLステートメント(から行数を報告しINSERTUPDATEDELETE、など)。
  • StatementCompletedイベントには、イベントを発生させたバッチからの個々のSQLステートメントは含まれませ。ただし、イベントハンドラーにはsender入力パラメーターとしてが送信されます。これはSqlCommandクエリバッチのであり、プロパティにキャストsenderSqlCommandてから調べると、そのバッチを確認できCommandTextます(これは以下の例に示されています)。

ドキュメントは、非常に私はショーは、このイベントの両方のために発射する例まで働いているように、この上スパースExecuteNonQueryExecuteScalarの両方のアドホッククエリやストアドプロシージャ(すなわちのためだけでなく、SqlCommand.CommandTypeText対をStoredProcedure:)

using System;
using System.Data;
using System.Data.SqlClient;

namespace StatementCompletedFiring
{
    class Program
    {
        static void Main(string[] args)
        {
            using (SqlConnection _Connection =
                          new SqlConnection("Integrated Security = True;"))
            {
                using (SqlCommand _Command = new SqlCommand(@"
SET NOCOUNT OFF; --  ensures that the 'StatementCompleted' event fires

EXEC('
CREATE PROCEDURE #TestProc
AS
SELECT * FROM sys.objects;

SELECT * FROM sys.tables;
');

SELECT * FROM sys.objects;
", _Connection))
                {

                    _Command.StatementCompleted += _Command_StatementCompleted;

                    try
                    {
                        _Connection.Open();

                        _Command.ExecuteNonQuery();

                        _Command.CommandText = @"
SELECT 123 AS [Bob];

WAITFOR DELAY '00:00:05.000'; --5 second pause to shows when the events fire

SELECT 2 AS [Sally]
UNION ALL
SELECT 5;
";
                        Console.WriteLine("\n\t");
                        Console.WriteLine(_Command.ExecuteScalar().ToString());
                        Console.WriteLine("\n");


                        _Command.CommandType = CommandType.StoredProcedure;
                        _Command.CommandText = "#TestProc";
                        _Command.ExecuteNonQuery();
                    }
                    catch (Exception _Exception)
                    {
                        throw new Exception(_Exception.Message);
                    }
                }
            }
        }

        static void _Command_StatementCompleted(object sender,
                                                StatementCompletedEventArgs e)
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.Write("\nQuery Batch: ");
            Console.ForegroundColor = ConsoleColor.White;
            Console.WriteLine(((SqlCommand)sender).CommandText);

            Console.ForegroundColor = ConsoleColor.Red;
            Console.Write("Row(s) affected: ");
            Console.ForegroundColor = ConsoleColor.White;
            Console.WriteLine(e.RecordCount.ToString() + "\n");

            Console.ResetColor();
        }
    }
}

出力:

クエリバッチ:
SET NOCOUNT OFF; -'StatementCompleted'イベントが発生することを確認します

EXEC( 'CREATE PROCEDURE #TestProc AS SELECT * FROM sys.objects;

SELECT * FROM sys.tables; ');

SELECT * FROM sys.objects;

影響を受ける行: 453

クエリバッチ:
SELECT 123 AS [Bob];

WAITFOR DELAY '00:00:05.000 '; --5秒の一時停止

SELECT 2 AS [サリー] UNION ALL SELECT 5;

影響を受ける行: 1

クエリバッチ:
SELECT 123 AS [Bob];

WAITFOR DELAY '00:00:05.000 '; --5秒の一時停止

SELECT 2 AS [サリー] UNION ALL SELECT 5;

影響を受ける行: 2

123

クエリバッチ: #TestProc
影響を受ける行: 453

クエリバッチ: #TestProc
影響を受ける行: 17


1
私はこれを試しました、そしてそれは私のために働きます。奇妙なことに、ストアドプロシージャ内のPRINTステートメントからのStatementCompletionsとInfoMessagesは互いに同期していないようです(StatementCompletionsの束を取得し、次にPRINTステートメントの出力を取得しますが、インターリーブされているはずです)。それは別の日のSSMSトリックだと思います...
RBarryYoung

1
@RBarryYoungはい、その動作は、たとえ煩わしくても予想されるものだと思います。これは、TDS(表形式のデータストリーム)内の項目の順序(msdn.microsoft.com/en-us/library/dd304523.aspx)に関係しています。PRINTおよびRAISERROR(..., 10, 1)メッセージはすべて結果セットの後に来ることを知っています。私はそのドキュメントでメッセージの順序を見つけようとしていますが、今のところそれに遭遇していません。
ソロモンルツキー2015年

私にとっての謎は、SSMSがそれを正しく分類する方法です。
RBarryYoung 2015年

1
@RBarryYoungおそらく、これは個別のクエリからの行数に関する問題なので、これは別の質問になるはずです。それは良い質問です、そして私はそれを理解しました:)。機会がありましたら、質問に投稿します。
ソロモンルツキー2015年

1
@RBarryYoung Yikes。申し訳ありません。あなたが元気であることを願っています。私は数日以内にそれに到達しようとします。最後のメッセージを投稿した後で考えたテストのバリエーションが1つまたは2つ残っています。ここへのリンクを掲載します。
ソロモンルッツキー2015年

-1

executenonqueryの結果は、ここで望んでいることを実行するだけではありません。しかし、そこに到達することはできます。それは、情報を何に使用するかによって異なります。

各挿入「PRINT @@ ROWCOUNT」の後にこの行を追加できます。出力(「bar」を取得する場所)の一部として、前の操作の影響を受けた行数を取得する必要があります。

または、ストアドプロシージャに「OUTPUT」パラメータを追加して結果を保持し、executenonqueryを実行したときにそれをキャプチャすることもできます。

編集:

Jonathan Kehasiasがまとめたサンプルをなんとか修正して、statementcompletedのイベント処理を含めました。この2行を追加するだけです。

#Add handler for StatementCompleted
$statementhandler = {param($sender, [System.Data.StatementCompletedEventArgs]$event) Write-Host $event.RecordCount };

#Attach handler...
$cmd.add_StatementCompleted($statementhandler)

これらの手順は変更できません。ExecuteNonQuery以外のものを使用するなど、クライアントコードを変更できます。
RBarryYoung

イベントハンドラーをsqlcommand infomessageイベントにアタッチしてみてください。この記事では、Powershellを使用してそれを行う方法を示します。 sqlskills.com/blogs/jonathan/…–
Jonathan Fite、

あなたが私の質問を読んでいたら、私がすでにそれをしているのを見たことがあるでしょう。そこにはありません
RBarryYoung 2015年

1
C#領域のこの質問は、SQLCommand.StatementCompletedイベントにリスナーを追加すると、彼らが探していたものを与えたと言っています。 stackoverflow.com/questions/27993049/...
ジョナサンFITE
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.