如何将大型SQL脚本解析为批处理?

5

我有一个非常大的SQL文件,我想将其分批执行。我希望确保我的解析方式与SSMS和SQLCMD相同。

微软提供了一个很棒的混合模式程序集,名为Microsoft.SqlServer.BatchParser,并带有一个名为Parser的类,看起来可以胜任此任务。

在调用Parse()之前,它需要IBatchSource的实现作为SetBatchSource的参数。

我在哪里可以找到IBatchSource的实现,并获取有关如何使用此功能的更多信息?

1个回答

14

我在GAC中找到了程序集Microsoft.SqlServer.BatchParser,以及它的伙伴Microsoft.SqlServer.BatchParserClient,其中包含接口IBatchSource的实现。

namespace Microsoft.SqlServer.Management.Common
{
  internal class BatchSourceFile : IBatchSource
  internal class BatchSourceString : IBatchSource
}
下面的对话随后发生。

程序集:你好!我的名字是Microsoft.SqlServer.Management.Common.ExecuteBatch。你想要调用StringCollection GetStatements(string sqlCommand)吗?

我:是的,我想要,BatchParserClient程序集。谢谢你的询问!

可重复操作指南(可以在家里尝试!)

  • 安装Microsoft SQL Server 2008 R2共享管理对象
  • 将Microsoft.SqlServer.BatchParser.dll和Microsoft.SqlServer.BatchParserClient.dll从GAC复制到您解决方案中的一个文件夹中。
  • 引用Microsoft.SqlServer.BatchParser和Microsoft.SqlServer.BatchParserClient

Program.cs

using System;
using System.Collections.Specialized;
using System.IO;
using System.Text;
using Microsoft.SqlServer.Management.Common;

namespace ScriptParser
{
   class Program
   {
      static void Main(string[] args)
      {
         ExecuteBatch batcher = new ExecuteBatch();
         string text = File.ReadAllText(@"Path_To_My_Long_Sql_File.sql");
         StringCollection statements = batcher.GetStatements(text);
         foreach (string statement in statements)
         {
            Console.WriteLine(statement);
         }
      }
   }
}

App.Config

的翻译是:

应用程序配置文件

<?xml version="1.0"?>
<configuration>
   <startup useLegacyV2RuntimeActivationPolicy="true">
      <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
   </startup>
</configuration>

另一种选择是使用ScriptDom,如此答案中所述:https://stackoverflow.com/a/32529415/26877

using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.SqlServer.TransactSql.ScriptDom;

namespace ScriptDomDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            TSql120Parser parser = new TSql120Parser(false);
            IList<ParseError> errors;
            using (StringReader sr = new StringReader(@"create table t1 (c1 int primary key)
GO
create table t2 (c1 int primary key)"))
            {
                TSqlFragment fragment = parser.Parse(sr, out errors);
                IEnumerable<string> batches = GetBatches(fragment);
                foreach (var batch in batches)
                {
                    Console.WriteLine(batch);
                }
            }
        }

        private static IEnumerable<string> GetBatches(TSqlFragment fragment)
        {
            Sql120ScriptGenerator sg = new Sql120ScriptGenerator();
            TSqlScript script = fragment as TSqlScript;
            if (script != null)
            {
                foreach (var batch in script.Batches)
                {
                    yield return ScriptFragment(sg, batch);
                }
            }
            else
            {
                // TSqlFragment is a TSqlBatch or a TSqlStatement
                yield return ScriptFragment(sg, fragment);
            }
        }

        private static string ScriptFragment(SqlScriptGenerator sg, TSqlFragment fragment)
        {
            string resultString;
            sg.GenerateScript(fragment, out resultString);
            return resultString;
        }
    }
}

抱歉,但是 File.ReadAllText 仍会先将整个文件读入内存。因此对于大文件(也许是不合理的大文件,但仍然存在问题)。基本上,需要一个 "流式" 接口。正如我在这里所指出的,SQLCMD.EXE 能够做到这一点,而且可能也使用了 IBatchSource,尽管是本地实现而不是 "托管" 包装器。问题在于:如何将输入文本分成块,以便 BatchParser 能够理解它。(+1是为了好贡献) - Christian.K
@Christian.K 发现了一个答案 https://stackoverflow.com/a/32529415/26877,使用 ScriptDOM 解析批处理文件可能是一个选项。 - JJS

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接