【问题标题】:SSMS SMO Objects: Get query resultsSSMS SMO 对象:获取查询结果
【发布时间】:2012-08-22 16:01:32
【问题描述】:

我遇到了this 教程,了解如何使用 GO 语句执行 SQL 脚本。
现在我想知道我可以得到消息 TAB 的输出。

使用多个 GO 语句,输出将如下所示:
1 行受影响
912 行受影响
...

但是 server.ConnectionContext.ExecuteNonQuery() 只能返回一个 int,而我需要所有文本。如果查询的某些部分有错误,它也应该把它放在输出中。 任何帮助,将不胜感激。

【问题讨论】:

    标签: c# sql-server ado.net ssms smo


    【解决方案1】:

    最简单的方法可能是打印您返回的ExecuteNonQuery 的号码:

    int rowsAffected = server.ConnectionContext.ExecuteNonQuery(/* ... */);
    if (rowsAffected != -1)
    {
         Console.WriteLine("{0} rows affected.", rowsAffected);
    }
    

    这应该可以,但不会遵守当前会话/范围的SET NOCOUNT 设置。

    否则,您将像使用“普通”ADO.NET 一样进行操作。不要使用ServerConnection.ExecuteNonQuery() 方法,而是通过访问底层SqlConnection 对象来创建SqlCommand 对象。在那订阅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 正是提到DONE_IN_PROC 事件时TDS 协议所谈论的内容;参见MSDN 上SET NOCOUNT 命令的Remarks。)

    就我个人而言,我在自己的“克隆”SQLCMD.EXE 中成功地使用了这种方法。

    更新:应该注意,这种方法(当然)需要您在GO 分隔符处手动拆分输入脚本/语句,因为您又回到了使用@987654340 @ 不能一次处理多个批次。为此,有多种选择:

    • 在以GO 开头的行上手动拆分输入(注意:GO 可以像GO 5 一样调用,例如,执行前一批 5 次)。
    • 使用ManagedBatchParser 类/库来帮助您将输入拆分为单个批次,尤其是使用上面的代码(或类似代码)实现ICommandExecutor.ProcessBatch

    我选择了后一个选项,这是相当多的工作,因为它没有很好的文档记录并且示例很少(谷歌一下,你会找到一些东西,或者使用反射器来查看 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() 返回的块,以便 解析器仍然可以使用它们(看起来它们需要完整的语句, 这首先会破坏解析的目的......)。

    【讨论】:

    • 这很好,但是 SQL 脚本中有 GO 关键字,如果我使用普通的 ADO.NET 它将显示为错误,我可以使用 GO 作为字符串将它们拆分,但仍然如此。有什么办法可以按照我想要的方式做吗?
    • 问题是“你想要的方式”可能不正确(在所有情况下)。在答案顶部查看我的更新。
    • @Christian.K 您在哪里找到有关使用 ManagedBatchParser 的信息或文档?我找不到 IBatchSource 的实现。它需要你自己做吗?
    • @JJS 当时,我也找不到任何实质性信息。我做了以下事情:在我知道的程序集中使用 dotPeek 进行探索,必须使用解析器(例如 SSMS 的一部分),第二次阅读文档并使用 SQLCMD.EXE 进行测试。后者尤其针对ICommandExecutorEdListVarServerList 等方法和IVariableResolver 的方法(类似于SQLCMD.EXE 中的:setvar 等)的行为。 关于实现IBatchSource,我会更新我的答案。
    • @Christian.K 感谢您的指点。似乎微软确实有一个 IBatchSource 的实现!从 SQL 2008 或 2012 查看 GAC 中的 Microsoft.SqlServer.BatchParserClient!我想在你做之前我最好把它规范化。 How do you parse large SQL scripts into batches?
    猜你喜欢
    • 2011-09-04
    • 2015-05-05
    • 2018-08-06
    • 2018-06-14
    • 2013-04-20
    • 2020-03-26
    • 1970-01-01
    • 2021-11-30
    • 1970-01-01
    相关资源
    最近更新 更多