我遇到了this教程,了解如何使用GO语句执行SQL脚本 现在我想知道我能得到消息TAB的输出。
使用几个GO语句,输出将如下:
1行受影响
受影响的912行
......
但是server.ConnectionContext.ExecuteNonQuery()只能返回一个int,而我需要所有的文本。如果查询的某些部分存在某些错误,则应将其也放在输出中。 任何帮助,将不胜感激。
答案 0 :(得分: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完全相同的优点:
SET NOCOUNT ON
,则根本不会调用它。SET NOCOUNT OFF
,则会为批处理中的每个语句调用它。(补充工具栏:看起来StatementCompleted
正是TDS协议在提到DONE_IN_PROC
事件时所说的内容;请参阅MSDN上的SET NOCOUNT命令的Remarks。)
就我个人而言,我已经在我自己的“克隆”SQLCMD.EXE中成功使用了这种方法。
更新:需要注意的是,这种方法(当然)要求您在GO
分隔符处手动拆分输入脚本/语句,因为您已经恢复使用SqlCommand.Execute*()
,一次无法处理多个批次。为此,有多种选择:
GO
开头的行手动拆分输入(警告:GO
可以像GO 5
一样调用,例如,执行前一批次5次。)我选择了后面的选项,这是相当多的工作,因为它没有很好的文档和示例很少(谷歌有点,你会找到一些东西,或使用反射器来看看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
)的这两种实现都存在一个问题,即它们会立即读取完整的文本
进入记忆。我有一个案例爆炸了,有一个巨大的(!)脚本与生成的INSERT
语句的gazillions。即使我认为这不是一个实际问题,SQLCMD.EXE
也可以处理它。但对于我的生活,我无法弄清楚究竟是怎样的,
你需要形成为IBatchParser.GetContent()
返回的块,以便...
解析器仍然可以使用它们(看起来它们需要是完整的语句,
这首先会破坏解析的目的......)。