C# SSMS SMO对象:获取查询结果

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

我偶然看到了本教程,以了解如何使用GO语句执行SQL脚本。
现在我想知道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
    可以像
    GO 5
    一样调用,例如,执行前一批5次)
  • 使用类/库帮助您将输入拆分为单个批处理,尤其是使用上面的代码(或类似代码)实现
我选择了后一个选项,这是一项相当多的工作,因为它没有很好的文档记录,而且示例也很少(谷歌搜索一下,您会发现一些东西,或者使用reflector查看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();
请注意,对于直接语句(
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!我想我最好在你之前把它封为圣典。