C# SSMS SMO对象:获取查询结果
我偶然看到了本教程,以了解如何使用GO语句执行SQL脚本。C# SSMS SMO对象:获取查询结果,c#,sql-server,ado.net,ssms,smo,C#,Sql Server,Ado.net,Ssms,Smo,我偶然看到了本教程,以了解如何使用GO语句执行SQL脚本。 现在我想知道messages选项卡的输出是什么 对于几个GO语句,输出如下: 1行受影响 912行受影响 ... 但是server.ConnectionContext.ExecuteNonQuery()只能返回int,而我需要所有文本。如果查询的某些部分出现错误,它应该将其也放在输出中。 任何帮助都将不胜感激 最简单的方法可能是只打印您为执行查询返回的号码: int rowsAffected = server.ConnectionC
现在我想知道messages选项卡的输出是什么 对于几个GO语句,输出如下:
1行受影响
912行受影响
... 但是server.ConnectionContext.ExecuteNonQuery()只能返回int,而我需要所有文本。如果查询的某些部分出现错误,它应该将其也放在输出中。
任何帮助都将不胜感激 最简单的方法可能是只打印您为执行查询返回的号码:
int rowsAffected = server.ConnectionContext.ExecuteNonQuery(/* ... */);
if (rowsAffected != -1)
{
Console.WriteLine("{0} rows affected.", rowsAffected);
}
这应该可以工作,但不符合当前会话/范围的SET NOCOUNT
设置
否则,您将像使用“普通”ADO.NET一样执行此操作。不要使用ServerConnection.ExecuteNonQuery()
方法,而是通过访问基础SqlConnection
对象来创建SqlCommand
对象。此时,订阅语句completed
事件
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();
}
使用语句completed
(例如,手动打印ExecuteNonQuery()
返回的值)的好处是,它的工作原理与SSMS或SQLCMD.EXE完全相同:
- 对于没有行数的命令,根本不会调用它(例如GO、USE)
- 如果设置了
,则根本不会调用它SET NOCOUNT ON
- 如果设置了
,则将为批处理中的每个语句调用它SET NOCOUNT OFF
语句completed
正是TDS协议在提到DONE\u IN_PROC
事件时所讨论的内容;请参阅MSDN上的SET NOCOUNT命令。)
就我个人而言,我已经在我自己的SQLCMD.EXE“克隆”中成功地使用了这种方法
更新:应该注意的是,这种方法(当然)需要您在GO
分隔符处手动拆分输入脚本/语句,因为您重新使用SqlCommand.Execute*()
,它不能一次处理多个批。为此,有多个选项:
- 手动拆分以
开头的行上的输入(注意:GO
可以像GO
一样调用,例如,执行前一批5次)GO 5
- 使用类/库帮助您将输入拆分为单个批处理,尤其是使用上面的代码(或类似代码)实现
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();
请注意,对于直接语句(statementbacksource
)或文件(FileBatchSource
),这两种实现都存在一个问题,即它们一次读取完整文本
进入记忆。我曾经遇到过一个这样的例子:一个巨大的(!)脚本,生成了无数的INSERT
语句。尽管我不认为这是一个实际问题,SQLCMD.EXE
可以处理它。但就我个人而言,我不知道到底是怎么回事,
您需要形成为IBatchParser.GetContent()
返回的块,以便
解析器仍然可以使用它们(看起来它们需要是完整的语句,
这可能会首先破坏解析的目的…。这很好,但SQL脚本中有GO关键字,如果我使用纯ADO.NET,它将显示为错误,我可以使用GO as字符串拆分它们,但仍然可以。有没有什么方法可以按照我想要的方式来做?问题是“你想要的方式”可能不是正确的(在所有情况下)。请参见答案顶部的“我的更新”。@Christian.K您在哪里找到有关使用ManagedBatchParser的信息或文档?我找不到IBatchSource的实现。需要你自己制作吗?@JJS当时,我也找不到任何实质性的信息。我做了以下工作:在我知道的程序集中使用dotPeek,必须使用解析器(例如SSM的一部分),然后阅读文档并使用
SQLCMD.EXE
进行测试。后者尤其适用于与Ed
、ListVar
、ServerList
等相关的行为ICommandExecutor
的方法和IVariableResolver
(类似于:setvar
,等等,来自SQLCMD.EXE
。关于IBatchSource
的实现,我将更新我的答案。@Christian.K感谢您的伟大指点。看起来微软确实有一个IBatchSource的实现!请从SQL 2008或20的GAC中查看Microsoft.SqlServer.BatchParserClient12!我想我最好在你之前把它封为圣典。