我通过本教程了解如何使用 GO 语句执行 SQL 脚本。
现在我想知道我可以得到消息 TAB 的输出。
使用多个 GO 语句,输出将如下所示:
1 行受影响
912 行受影响
......
但是 server.ConnectionContext.ExecuteNonQuery() 只能返回一个 int,而我需要所有文本。如果查询的某些部分出现错误,它也应该将其放在输出中。任何帮助,将不胜感激。
最简单的事情可能是只打印您返回的号码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 完全一样:
SET NOCOUNT ON
已设置,则根本不会调用它。SET NOCOUNT OFF
已设置,它将为批处理中的每个语句调用。(侧边栏:看起来StatementCompleted
正是 TDS 协议在DONE_IN_PROC
提到事件时所谈论的内容;请参阅MSDN 上的 SET NOCOUNT 命令的备注。)
就个人而言,我在自己的 SQLCMD.EXE 的“克隆”中成功地使用了这种方法。
更新:应该注意的是,这种方法(当然)需要您在GO
分隔符处手动拆分输入脚本/语句,因为您又回到使用SqlCommand.Execute*()
它不能一次处理多个批次。为此,有多种选择:
GO
手动拆分以(caveat:开头的行的输入GO
,GO 5
例如,执行前一批 5 次)。我选择后一个选项,这是相当多的工作,因为它没有很好的文档记录并且示例很少(谷歌一下,你会找到一些东西,或者使用反射器来查看 SMO-Assemblies 如何使用该类) .
使用 的好处(也可能是负担)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
),这两种实现都存在将完整文本一次读入内存的问题。我有一个案例爆炸了,有一个巨大的(!)脚本,其中包含大量生成的INSERT
语句。尽管我认为这不是一个实际问题,SQLCMD.EXE
但可以处理它。但是对于我的一生,我无法弄清楚你需要如何形成返回的块,IBatchParser.GetContent()
以便解析器仍然可以使用它们(看起来它们需要是完整的语句,这有点像首先击败解析的目的......)。