最も簡単なことは、おそらくあなたが戻ってきた番号を印刷することです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*()
これには、複数のオプションがあります。
後者のオプションを選択しますが、これは十分に文書化されておらず、例がまれであることを考えると、かなりの作業でした(少しグーグル、いくつかのものを見つけるか、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()
、パーサーが引き続きそれらを処理できるように、返されるチャンクを形成する必要があるかどうかを正確に理解することはできませんでした(完全なステートメントである必要があるように見えます。そもそも構文解析の目的を打ち負かしてください...)。