2013-04-29 8 views
5

Ho un file sql molto grande che voglio suddividere in batch per l'esecuzione. Voglio assicurarmi di analizzarlo allo stesso modo di SSMS e SQLCMD.Come si analizzano gli script SQL di grandi dimensioni in batch?

Microsoft dispone di un ottimo assembly in modalità mista denominato Microsoft.SqlServer.BatchParser con una classe denominata Parser che sembra quasi farebbe il trucco.

Desidera un'implementazione di IBatchSource come argomento su SetBatchSource prima di chiamare Parse().

Dove è possibile trovare un'implementazione di IBatchSource e ulteriori informazioni su come utilizzare questa funzionalità?

risposta

12

Ho trovato l'assembly Microsoft.SqlServer.BatchParser nel GAC insieme al suo amico Microsoft.SqlServer.BatchParserClient che contiene le implementazioni dell'interfaccia IBatchSource.

namespace Microsoft.SqlServer.Management.Common 
{ 
    internal class BatchSourceFile : IBatchSource 
    internal class BatchSourceString : IBatchSource 
} 

Si è quindi verificata la seguente conversazione.

Assemblea: Ciao! Il mio nome è Microsoft.SqlServer.Management.Common.ExecuteBatch. Vorresti StringCollection GetStatements (stringa sqlCommand)?

Me: Sì, vorrei, assemblea BatchParserClient. Grazie per averlo chiesto!

Istruzioni ripetibili (provate a casa!)

  • Installare Microsoft SQL Server 2008 R2 Shared Management Objects
  • Copia Microsoft.SqlServer.BatchParser.dll e Microsoft.SqlServer.BatchParserClient.dll dal GAC ad un cartella nella tua soluzione.
  • Riferimento 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> 

Un'altra opzione è quella di utilizzare ScriptDom come descritto in questa risposta: 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; 
     } 
    } 
} 
+0

Siamo spiacenti, ma 'File.ReadAllText' continuerà a leggere l'intero file in memoria prima. Quindi con file di grandi dimensioni (forse irragionevolmente grandi, ma comunque), hai problemi. Fondamentalmente, sarebbe necessaria un'interfaccia "streaming". Come ho notato [qui sopra] (http://stackoverflow.com/a/12154400/21567), 'SQLCMD.EXE' è in grado di farlo, e presumibilmente usa anche' IBatchSource', anche se l'implementazione nativa e non il wrapper "gestito". La domanda rimane: come dividere il testo di input in blocchi in modo che BatchParser possa capirlo. (+1 anche se ancora, per un buon contributo) –

+0

@ Christian.K trovato risposta https: // stackoverflow.it/a/32529415/26877 che potrebbe essere un'opzione per analizzare i tuoi batch usando ScriptDOM. – JJS

Problemi correlati