2012-08-28 105 views
3

我遇到了this教程,以了解如何使用GO语句执行SQL脚本。
现在我想知道什么可以得到消息选项卡的输出。SSMS SMO对象:获取查询结果

与几个GO语句,输出会是这样:
1行受到影响
912行受到影响
...

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

回答

4

最简单的事情是可能只打印你回来了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中取得成功。

UPDATE:应该指出的是,这种方法(当然)需要您手动在GO分离拆分输入脚本/语句,因为你回来使用SqlCommand.Execute*()无法处理在多批次时间。为此,可以有多种选择:

  • 手动分割上开始GO线输入(警告:GO可以称为像GO 5,例如,以执行前一批5次)。
  • 使用ManagedBatchParser类/库来帮助您将输入拆分为单个批次,特别是使用上述代码(或类似的代码)实现ICommandExecutor.ProcessBatch

我选择后面的选项,这是相当有些工作,因为它不是很好的记录和例子很少(谷歌有点,你会发现一些东西,或使用反射器看SMO - 组件使用该类)。

的利益(也许负担)使用ManagedBatchParser的是,它还将解析T-SQL脚本的所有其他结构(用于SQLCMD.EXE)为您。包括::setvar,:connect,:quit等。当然,如果您的脚本不使用它们,则不必实施各自的ICommandExecutor成员。但请注意,您可能无法执行“任意”脚本。

那么,是否这样做让你。从如何打印“......受到影响的行”的“简单问题”到这样一个事实,即以一种强大而普遍的方式进行这项工作并不轻松(因为需要后台工作)。 YMMV,祝你好运。在ManagedBatchParser使用

更新

似乎有关于如何实现IBatchSource没有好documenation或例子,这里是我跟去了。

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(),这样 解析器仍然可以与它们一起工作(看起来他们需要完整的语句, 这首先会破坏解析的目的......)。

+0

它是好的,但在SQL脚本中有GO关键字,如果我使用普通的ADO.NET,它会将其显示为错误,我可以使用GO将它们拆分为字符串,但仍然存在。有没有办法以我想要的方式去做? –

+0

问题是,“你想要的方式”可能不正确(在所有情况下)。在答案顶部查看我的更新。 –

+0

@ Christian.K您在哪里找到有关使用ManagedBatchParser的信息或文档?我找不到IBatchSource的实现。它是否需要你自己做? – JJS