4

GOステートメントを使用してSQLスクリプトを実行する方法を理解するために、このチュートリアルに出くわしました。
ここで、メッセージTABの出力を取得できるものを知りたいです。

いくつかのGOステートメントを使用すると、出力は次のようになります
。1行が影響を受ける
912行が影響を受ける
...

しかし、server.ConnectionContext.ExecuteNonQuery()はintのみを返すことができますが、すべてのテキストが必要です。クエリの一部にエラーがある場合は、それを出力にも含める必要があります。どんな助けでもいただければ幸いです。

4

1 に答える 1

4

最も簡単なことは、おそらくあなたが戻ってきた番号を印刷することですExecuteNonQuery

int rowsAffected = server.ConnectionContext.ExecuteNonQuery(/* ... */);
if (rowsAffected != -1)
{
     Console.WriteLine("{0} rows affected.", rowsAffected);
}

SET NOCOUNTこれは機能するはずですが、現在のセッション/スコープの設定を尊重しません。

それ以外の場合は、「プレーンな」ADO.NETの場合と同じように実行します。ServerConnection.ExecuteNonQuery()メソッドを使用せずにSqlCommand、基になるオブジェクトにアクセスしてオブジェクトを作成しSqlConnectionます。その上でイベントを購読しStatementCompletedます。

using (SqlCommand command = server.ConnectionContext.SqlConnectionObject.CreateCommand())
{
    // Set other properties for "command", like StatementText, etc.

    command.StatementCompleted += (s, e) => {
         Console.WriteLine("{0} row(s) affected.", e.RecordCount);
    };

    command.ExecuteNonQuery();
}

使用するStatementCompletedと(たとえば、返された値を手動でExecuteNonQuery()出力する)、SSMSまたはSQLCMD.EXEとまったく同じように機能するという利点があります。

  • ROWCOUNTを持たないコマンドの場合、それはまったく呼び出されません(GO、USEなど)。
  • が設定されている場合SET NOCOUNT ONは、まったく呼び出されません。
  • が設定されている場合SET NOCOUNT OFF、バッチ内のすべてのステートメントに対して呼び出されます。

(サイドバー:イベントが言及されStatementCompletedたときにTDSプロトコルが正確に話しているように見えます。MSDNのSET NOCOUNTコマンドの注釈を参照してください。)DONE_IN_PROC

個人的には、このアプローチを使用して、SQLCMD.EXEの独自の「クローン」で成功しました。

更新:このアプローチでは(もちろん) 、一度に複数のバッチを処理できないGO使用に戻るため、セパレーターで入力スクリプト/ステートメントを手動で分割する必要があることに注意してください。SqlCommand.Execute*()これには、複数のオプションがあります。

  • で始まる行の入力を手動で分割しますGO(注意:たとえば、前のバッチを5回実行するGOように呼び出すことができます)。GO 5
  • ManagedBatchParserクラス/ライブラリを使用して、入力を単一のバッチに分割します。特に、上記のコード(またはそれに類似したもの)を使用してICommandExecutor.ProcessBatchを実装します。

後者のオプションを選択しますが、これは十分に文書化されておらず、例がまれであることを考えると、かなりの作業でした(少しグーグル、いくつかのものを見つけるか、SMOアセンブリがそのクラスをどのように使用するかを確認するためにリフレクターを使用します) 。

を使用する利点(およびおそらく負担)はManagedBatchParser、T-SQLスクリプト(を対象とする)の他のすべての構成も解析するSQLCMD.EXEことです。:setvar、、、など:connectを含みます。もちろん、スクリプトでメンバーを使用しない場合:quitは、それぞれのメンバーを実装する必要はありません。ICommandExecutorただし、「任意の」スクリプトを実行できない場合があることに注意してください。

さて、それはあなたを置きましたか。「影響を受ける行...」を印刷する方法の「単純な質問」から、堅牢で一般的な方法で行うのは簡単ではないという事実まで(必要なバックグラウンド作業を前提として)。YMMV、頑張ってください。

ManagedBatchParserの使用に関する更新

実装方法についての良い文書や例はないようIBatchSourceです。これが私が行ったものです。

internal abstract class BatchSource : IBatchSource
{
    private string m_content;

    public void Populate()
    {
        m_content = GetContent();
    }

    public void Reset()
    {
        m_content = null;
    }

    protected abstract string GetContent();

    public ParserAction GetMoreData(ref string str)
    {
        str = null;

        if (m_content != null)
        {
            str = m_content;
            m_content = null;
        }

        return ParserAction.Continue;
    }
}

internal class FileBatchSource : BatchSource
{
    private readonly string m_fileName;

    public FileBatchSource(string fileName)
    {
        m_fileName = fileName;
    }

    protected override string GetContent()
    {
        return File.ReadAllText(m_fileName);
    }
}

internal class StatementBatchSource : BatchSource
{
    private readonly string m_statement;

    public StatementBatchSource(string statement)
    {
        m_statement = statement;
    }

    protected override string GetContent()
    {
        return m_statement;
    }
}

そして、これはあなたがそれをどのように使うかです:

var source = new StatementBatchSource("SELECT GETUTCDATE()");
source.Populate();

var parser = new Parser(); 
parser.SetBatchSource(source);
/* other parser.Set*() calls */

parser.Parse();

直接ステートメント(StatementBatchSource)またはファイル(FileBatchSource)の両方の実装には、完全なテキストを一度にメモリに読み込むという問題があることに注意してください。何億ものステートメントが生成された巨大な(!)スクリプトがあり、それが爆発したケースが1つありましたINSERT。それは実際的な問題ではないと思いますが、SQLCMD.EXE対処できます。しかし、私の人生ではIBatchParser.GetContent()、パーサーが引き続きそれらを処理できるように、返されるチャンクを形成する必要があるかどうかを正確に理解することはできませんでした(完全なステートメントである必要があるように見えます。そもそも構文解析の目的を打ち負かしてください...)。

于 2012-08-28T07:08:28.657 に答える