Führen Sie ein großes SQL-Skript aus (mit GO-Befehlen).

89

Ich muss eine große Anzahl von SQL-Anweisungen (Erstellen einer Reihe von Tabellen, Ansichten und gespeicherten Prozeduren) in einem C # -Programm ausführen.

Diese Anweisungen müssen durch GOAnweisungen getrennt werden , SqlCommand.ExecuteNonQuery()mögen jedoch keine GOAnweisungen. Meine Lösung, die ich vermutlich als Referenz veröffentlichen werde, bestand darin, die SQL-Zeichenfolge in GOZeilen aufzuteilen und jeden Stapel separat auszuführen.

Gibt es einen einfacheren / besseren Weg?

Blorgbeard ist raus
quelle

Antworten:

105

Verwenden Sie SQL Server Management Objects (SMO), die GO-Trennzeichen verstehen. Siehe meinen Blog-Beitrag hier: http://weblogs.asp.net/jongalloway/Handling-_2200_GO_2200_-Separators-in-SQL-Scripts- 2D00 -the-easy-way

Beispielcode:

public static void Main()    
{        
  string scriptDirectory = "c:\\temp\\sqltest\\";
  string sqlConnectionString = "Integrated Security=SSPI;" +
  "Persist Security Info=True;Initial Catalog=Northwind;Data Source=(local)";
  DirectoryInfo di = new DirectoryInfo(scriptDirectory);
  FileInfo[] rgFiles = di.GetFiles("*.sql");
  foreach (FileInfo fi in rgFiles)
  {
        FileInfo fileInfo = new FileInfo(fi.FullName);
        string script = fileInfo.OpenText().ReadToEnd();
        using (SqlConnection connection = new SqlConnection(sqlConnectionString))
        {
            Server server = new Server(new ServerConnection(connection));
            server.ConnectionContext.ExecuteNonQuery(script);
        }
   }
}

Wenn dies bei Ihnen nicht funktioniert, lesen Sie die Bibliothek von Phil Haack, die dies behandelt: http://haacked.com/archive/2007/11/04/a-library-for-executing-sql-scripts-with-go-separators -und.aspx

Jon Galloway
quelle
2
Wie kann dies in eine Transaktion integriert werden? Der Code löst beim Erstellen der ServerConnection mit SqlConnection eine InvalidOperationException aus, für die eine Transaktion aussteht.
benPearce
1
Diese Lösung funktioniert. Ich möchte nur hinzufügen, dass Sie, wenn Sie Transaktionen mit einem TransactionScopeObjekt verwenden möchten, nur die Verbindung mit der aktuellen Umgebungstransaktion herstellen müssen. Überprüfen Sie meine Antwort hier: stackoverflow.com/a/18322938/1268570
Jupaol
funktioniert super, aber können wir verwenden SqlConnection.InfoMessage), um das Ergebnis in der C # -Anwendung zu sehen oder das Ergebnis in einer txtDatei zu speichern, nur um zu wissen, ob das Skript erfolgreich ausgeführt wurde, da sqlcmdich kürzlich nach 55 Minuten eine 150-MB-Skriptdatei auf einem Remote-Host ausgeführt habe Zeilen wurden mit diesem Fehler bewirkt, TCP Provider: An existing connection was forcibly closed by the remote host., communication link failure. , keine der betroffenen Zeilen kann bekannt sein, aber ich bin besorgt über Fehlermeldungen beim Ausführen einer datenbankgenerierten Skriptdatei.
Shaijut
5
Diese Lösungen verursachten einen Fehler in Ihrem Code, wenn einige SQL-DLLs nicht auf dem Computer installiert sind. .NET verwendet einige in Windows integrierte DLLs. Das Fehlen einiger SQL-Feature-Packs (einschließlich Verwaltungsobjekte) kann verhindern, dass Fehler wie "Microsoft.SqlServer.SqlClrProvider.dll" nicht gefunden werden. Das Beheben des Problems (es ist keine leichte Aufgabe) ist der nächste Fehler "Microsoft.SqlServer.BathParser.dll" usw. Suchen Sie nach einer anderen Lösung, um die Flexibilität Ihrer Anwendung sicherzustellen.
Alexandr Sargsyan
35

Dies ist, was ich zusammen geklopft habe, um mein unmittelbares Problem zu lösen.

private void ExecuteBatchNonQuery(string sql, SqlConnection conn) {
    string sqlBatch = string.Empty;
    SqlCommand cmd = new SqlCommand(string.Empty, conn);
    conn.Open();
    sql += "\nGO";   // make sure last batch is executed.
    try {
        foreach (string line in sql.Split(new string[2] { "\n", "\r" }, StringSplitOptions.RemoveEmptyEntries)) {
            if (line.ToUpperInvariant().Trim() == "GO") {
                cmd.CommandText = sqlBatch;
                cmd.ExecuteNonQuery();
                sqlBatch = string.Empty;
            } else {
                sqlBatch += line + "\n";
            }
        }            
    } finally {
        conn.Close();
    }
}

Es erfordert, dass GO-Befehle in einer eigenen Zeile stehen und keine Blockkommentare erkennen. Daher wird diese Art von Dingen aufgeteilt und verursacht einen Fehler:

ExecuteBatchNonQuery(@"
    /*
    GO
    */", conn);
Blorgbeard ist raus
quelle
Es ist schön, dass ich dies bei Bedarf einfach an SqlCe anpassen kann - der andere Code verwendet SQL-Verbindungsklassen und -Befehle.
Blue Toque
Ich möchte diesen Code mit einem SQL-Skript mit mehreren darin gespeicherten Prozeduren ausführen, bin aber etwas verwirrt. Wo liest er SQL? Wenn Sie sich auf "letzter Stapel" beziehen, meinen Sie damit den SQL-Code? Und wenn ja, wie würden Sie den letzten Stapel bestimmen und was wäre, wenn ich alle Stapel ausführen möchte, nicht nur den letzten? Ich kenne zu viele Fragen, aber danke, wenn Sie die Zeit haben, sie zu beantworten.
user1676874
Sie übergeben die SQL als Zeichenfolge an die Funktion: string sql- Das ist das gesamte Skript. Wenn ich mich auf einen "Stapel" beziehe, meine ich einen Teil des SQL-Codes zwischen zwei "GO" -Anweisungen. Der Code fügt GOam Ende des Skripts ein a hinzu, sodass der Code im Skript foreachden letzten Stapel nicht überspringt, wenn Sie Ihr Skript nicht mit einem beendet haben GO. Der geschriebene Code führt also das gesamte SQL aus.
Blorgbeard ist
Ich habe eine Erweiterungsmethode erstellt: interne statische Klasse SqlCommandHelper {interne statische Leere ExecuteBatchNonQuery (diese SqlCommand cmd, Zeichenfolge sql)
Rob Sedgwick

1
Wenn Sie ein bisschen effizienter sein möchten, können Sie StringBuilder sqlBatchstattdessen verwenden.
Lii

11

Sie können dazu SQL-Verwaltungsobjekte verwenden . Dies sind dieselben Objekte, mit denen Management Studio Abfragen ausführt. Ich glaube Server.ConnectionContext.ExecuteNonQuery(), Sie werden das tun, was Sie brauchen.


6

Das Batch-Trennzeichen-Schlüsselwort "GO" wird tatsächlich von SQL Management Studio selbst verwendet, damit es weiß, wo die an den Server gesendeten Batches beendet werden sollen, und es wird nicht an den SQL Server übergeben. Sie können das Schlüsselwort sogar in Management Studio ändern, wenn Sie dies wünschen.


5

Ich schaue mir das ein paar mal am Ende mit EF-Implementierung an. Ein bisschen modifiziert fürSqlConnection

public static void ExecuteSqlScript(this SqlConnection sqlConnection, string sqlBatch)
        {
            // Handle backslash utility statement (see http://technet.microsoft.com/en-us/library/dd207007.aspx)
            sqlBatch = Regex.Replace(sqlBatch, @"\\(\r\n|\r|\n)", string.Empty);

            // Handle batch splitting utility statement (see http://technet.microsoft.com/en-us/library/ms188037.aspx)
            var batches = Regex.Split(
                sqlBatch,
                string.Format(CultureInfo.InvariantCulture, @"^\s*({0}[ \t]+[0-9]+|{0})(?:\s+|$)", BatchTerminator),
                RegexOptions.IgnoreCase | RegexOptions.Multiline);

            for (int i = 0; i < batches.Length; ++i)
            {
                // Skip batches that merely contain the batch terminator
                if (batches[i].StartsWith(BatchTerminator, StringComparison.OrdinalIgnoreCase) ||
                    (i == batches.Length - 1 && string.IsNullOrWhiteSpace(batches[i])))
                {
                    continue;
                }

                // Include batch terminator if the next element is a batch terminator
                if (batches.Length > i + 1 &&
                    batches[i + 1].StartsWith(BatchTerminator, StringComparison.OrdinalIgnoreCase))
                {
                    int repeatCount = 1;

                    // Handle count parameter on the batch splitting utility statement
                    if (!string.Equals(batches[i + 1], BatchTerminator, StringComparison.OrdinalIgnoreCase))
                    {
                        repeatCount = int.Parse(Regex.Match(batches[i + 1], @"([0-9]+)").Value, CultureInfo.InvariantCulture);
                    }

                    for (int j = 0; j < repeatCount; ++j)
                    {
                       var command = sqlConnection.CreateCommand();
                       command.CommandText = batches[i];
                       command.ExecuteNonQuery();
                    }
                }
                else
                {
                    var command = sqlConnection.CreateCommand();
                    command.CommandText = batches[i];
                    command.ExecuteNonQuery();
                }
            }
        }

Vielen Dank, dass Sie @Filip Cordas. Obwohl dies nicht als Antwort markiert ist, hat mir das wie ein Zauber geholfen! Wir hatten eine große Anzahl von Skripten, in denen der BatchTerminator auf verschiedene Arten erwähnt wurde, wie Kombinationen von Groß- und Kleinbuchstaben (go, Go, GO usw.), und maximale Zeiten hatten nachgestellte oder führende Leerzeichen, was ein großes Problem bei der Ausführung über c # ... verursachte. . Danke !!
DipakRiswadkar

2
@DipakRiswadkar Ja, diese Frage wurde einige Male gesperrt, und keine der Antworten entsprach meinen Anforderungen. Daher sah die EF-Implementierung gut aus, sodass ich die Antwort veröffentlichte.
Filip Cordas

Tolle Antwort, es funktioniert wie ein Zauber, vielen Dank
cuongle

@Really sollte es auch dem Entity Framework-Team melden. Wie gesagt, dies war nur eine Kopie der Vergangenheit mit kleinen Änderungen.
Filip Cordas


4

Basierend auf der Lösung von Blorgbeard.

foreach (var sqlBatch in commandText.Split(new[] { "GO" }, StringSplitOptions.RemoveEmptyEntries))
{
   sqlCommand.CommandText = sqlBatch;
   sqlCommand.ExecuteNonQuery();
}

3
new [] {"GO", "Go", "go"}
Andrew Veriga
1
new [] {"GO", "Go", "go", "gO"}
Brandon Ward
Funktioniert, solange Sie die beiden Buchstaben in Ihrem Code nicht anderweitig verwenden können, z. B. GOTO-Anweisungen oder Kommentare.
Patrik
3

Wenn Sie SMO nicht verwenden möchten, z. B. weil Sie plattformübergreifend sein müssen, können Sie auch die ScriptSplitter-Klasse von SubText verwenden.

Hier ist die Implementierung in C # & VB.NET

Verwendung:

    string strSQL = @"
SELECT * FROM INFORMATION_SCHEMA.columns
GO
SELECT * FROM INFORMATION_SCHEMA.views
";

    foreach(string Script in new Subtext.Scripting.ScriptSplitter(strSQL ))
    {
        Console.WriteLine(Script);
    }

Wenn Sie Probleme mit mehrzeiligen Kommentaren im C-Stil haben, entfernen Sie die Kommentare mit Regex:

static string RemoveCstyleComments(string strInput)
{
    string strPattern = @"/[*][\w\d\s]+[*]/";
    //strPattern = @"/\*.*?\*/"; // Doesn't work
    //strPattern = "/\\*.*?\\*/"; // Doesn't work
    //strPattern = @"/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/ "; // Doesn't work
    //strPattern = @"/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/ "; // Doesn't work

    // http://stackoverflow.com/questions/462843/improving-fixing-a-regex-for-c-style-block-comments
    strPattern = @"/\*(?>(?:(?>[^*]+)|\*(?!/))*)\*/";  // Works !

    string strOutput = System.Text.RegularExpressions.Regex.Replace(strInput, strPattern, string.Empty, System.Text.RegularExpressions.RegexOptions.Multiline);
    Console.WriteLine(strOutput);
    return strOutput;
} // End Function RemoveCstyleComments

Das Entfernen einzeiliger Kommentare finden Sie hier:

https://stackoverflow.com/questions/9842991/regex-to-remove-single-line-sql-comments
Stefan Steiger
quelle
Betrachtet diese Klasse den /* Go */Fall?
Edgarmtze
@cMinor: Nicht im Splitter, aber Sie können mehrzeilige Kommentare mit Regex entfernen, bevor Sie teilen.
Stefan Steiger
2

Ich hatte auch das gleiche Problem, und ich konnte keinen anderen Weg finden, als die einzelne SQL-Operation in separate Dateien aufzuteilen und dann alle nacheinander auszuführen.

Offensichtlich liegt das Problem nicht bei Listen von DML-Befehlen, sondern können ohne GO dazwischen ausgeführt werden. andere Geschichte mit DDL (erstellen, ändern, löschen ...)

ila
quelle
2

Wenn Sie nicht auf die SMO-Route gehen möchten, können Sie "GO" nach ";" suchen und ersetzen. und die Abfrage wie Sie würden. Beachten Sie, dass nur die letzte Ergebnismenge zurückgegeben wird.

Jason Saldo
quelle
1
Sie führen ExecuteNonQuery aus. Dies ist bei weitem der einfachere Weg.
DaveMorganTexas
3
Mit "GO" können Sie dieselben Variablen im nächsten Befehl im Stapel erneut definieren. Das Platzieren eines Semikolons reicht nicht aus.
DDRider62
2

Ich habe dies heute erreicht, indem ich mein SQL aus einer Textdatei in eine Zeichenfolge geladen habe. Ich habe dann die String-Split-Funktion verwendet, um den String in einzelne Befehle zu unterteilen, die dann einzeln an den Server gesendet wurden. Simples :)

Ich habe gerade festgestellt, dass Sie sich auf \ nGO aufteilen müssen, nur für den Fall, dass die Buchstaben GO in einem Ihrer Tabellennamen usw. erscheinen. Ich schätze, ich hatte Glück!

Andy Dove
quelle
2

Wenn Sie SMO nicht verwenden möchten (was besser ist als die unten stehende Lösung, aber ich möchte eine Alternative angeben ...), können Sie Ihre Abfrage mit dieser Funktion aufteilen.

Es ist:

  • Kommentarbeweis (Beispiel --GO oder / * GO * /)
  • Funktioniert nur in einer neuen Zeile, genau wie in SSMS (Beispiel / * test / * GO funktioniert und wählen Sie 1 als nicht
  • String Proof (Beispieldruck 'no go')

    private List<string> SplitScriptGo(string script)
    {
        var result = new List<string>();
        int pos1 = 0;
        int pos2 = 0;
        bool whiteSpace = true;
        bool emptyLine = true;
        bool inStr = false;
        bool inComment1 = false;
        bool inComment2 = false;
    
        while (true)
        {
            while (pos2 < script.Length && Char.IsWhiteSpace(script[pos2]))
            {
                if (script[pos2] == '\r' || script[pos2] == '\n')
                {
                    emptyLine = true;
                    inComment1 = false;
                }
    
                pos2++;
            }
    
            if (pos2 == script.Length)
                break;
    
            bool min2 = (pos2 + 1) < script.Length;
            bool min3 = (pos2 + 2) < script.Length;
    
            if (!inStr && !inComment2 && min2 && script.Substring(pos2, 2) == "--")
                inComment1 = true;
    
            if (!inStr && !inComment1 && min2 && script.Substring(pos2, 2) == "/*")
                inComment2 = true;
    
            if (!inComment1 && !inComment2 && script[pos2] == '\'')
                inStr = !inStr;
    
            if (!inStr && !inComment1 && !inComment2 && emptyLine
                && (min2 && script.Substring(pos2, 2).ToLower() == "go")
                && (!min3 || char.IsWhiteSpace(script[pos2 + 2]) || script.Substring(pos2 + 2, 2) == "--" || script.Substring(pos2 + 2, 2) == "/*"))
            {
                if (!whiteSpace)
                    result.Add(script.Substring(pos1, pos2 - pos1));
    
                whiteSpace = true;
                emptyLine = false;
                pos2 += 2;
                pos1 = pos2;
            }
            else
            {
                pos2++;
                whiteSpace = false;
    
                if (!inComment2)
                    emptyLine = false;
            }
    
            if (!inStr && inComment2 && pos2 > 1 && script.Substring(pos2 - 2, 2) == "*/")
                inComment2 = false;
        }
    
        if (!whiteSpace)
            result.Add(script.Substring(pos1));
    
        return result;
    }
Bigjim
quelle
1

Verwenden Sie die folgende Methode, um die Zeichenfolge zu teilen und Stapel für Stapel auszuführen

using System;
using System.IO;
using System.Text.RegularExpressions;
namespace RegExTrial
{
    class Program
    {
        static void Main(string[] args)
        {
            string sql = String.Empty;
            string path=@"D:\temp\sample.sql";
            using (StreamReader reader = new StreamReader(path)) {
                sql = reader.ReadToEnd();
            }            
            //Select any GO (ignore case) that starts with at least 
            //one white space such as tab, space,new line, verticle tab etc
            string pattern="[\\s](?i)GO(?-i)";

            Regex matcher = new Regex(pattern, RegexOptions.Compiled);
            int start = 0;
            int end = 0;
            Match batch=matcher.Match(sql);
            while (batch.Success) {
                end = batch.Index;
                string batchQuery = sql.Substring(start, end - start).Trim();
                //execute the batch
                ExecuteBatch(batchQuery);
                start = end + batch.Length;
                batch = matcher.Match(sql,start);
            }

        }

        private static void ExecuteBatch(string command)
        { 
            //execute your query here
        }

    }
}
Sriwantha Attanayake
quelle
1

Um Dritte, reguläre Ausdrücke, Speicherkosten und schnelle Arbeit mit großen Skripten zu vermeiden, habe ich meinen eigenen Stream-basierten Parser erstellt. Es

  • prüft vorher die Syntax
  • kann Kommentare mit - oder / ** / erkennen

    -- some commented text
     /*
    drop table Users;
    GO
       */
  • kann String-Literale mit 'oder "erkennen

    set @s =
        'create table foo(...);
        GO
        create index ...';
  • behält die LF- und CR-Formatierung bei
  • behält den Kommentarblock in Objektkörpern bei (gespeicherte Prozeduren, Ansichten usw.)
  • und andere Konstruktionen wie

          gO -- commented text

Wie benutzt man

    try
    {
        using (SqlConnection connection = new SqlConnection("Integrated Security=SSPI;Persist Security Info=True;Initial Catalog=DATABASE-NAME;Data Source=SERVER-NAME"))
        {
            connection.Open();

            int rowsAffected = SqlStatementReader.ExecuteSqlFile(
                "C:\\target-sql-script.sql",
                connection,
                // Don't forget to use the correct file encoding!!!
                Encoding.Default,
                // Indefinitely (sec)
                0
            );
        }
    }
    // implement your handlers
    catch (SqlStatementReader.SqlBadSyntaxException) { }
    catch (SqlException) { }
    catch (Exception) { }

Stream-basierter SQL-Skriptleser

class SqlStatementReader
{
    public class SqlBadSyntaxException : Exception
    {
        public SqlBadSyntaxException(string description) : base(description) { }
        public SqlBadSyntaxException(string description, int line) : base(OnBase(description, line, null)) { }
        public SqlBadSyntaxException(string description, int line, string filePath) : base(OnBase(description, line, filePath)) { }
        private static string OnBase(string description, int line, string filePath)
        {
            if (filePath == null)
                return string.Format("Line: {0}. {1}", line, description);
            else
                return string.Format("File: {0}\r\nLine: {1}. {2}", filePath, line, description);
        }
    }

    enum SqlScriptChunkTypes
    {
        InstructionOrUnquotedIdentifier = 0,
        BracketIdentifier = 1,
        QuotIdentifierOrLiteral = 2,
        DblQuotIdentifierOrLiteral = 3,
        CommentLine = 4,
        CommentMultiline = 5,
    }

    StreamReader _sr = null;
    string _filePath = null;
    int _lineStart = 1;
    int _lineEnd = 1;
    bool _isNextChar = false;
    char _nextChar = '\0';

    public SqlStatementReader(StreamReader sr)
    {
        if (sr == null)
            throw new ArgumentNullException("StreamReader can't be null.");

        if (sr.BaseStream is FileStream)
            _filePath = ((FileStream)sr.BaseStream).Name;

        _sr = sr;
    }

    public SqlStatementReader(StreamReader sr, string filePath)
    {
        if (sr == null)
            throw new ArgumentNullException("StreamReader can't be null.");

        _sr = sr;
        _filePath = filePath;
    }

    public int LineStart { get { return _lineStart; } }
    public int LineEnd { get { return _lineEnd == 1 ? _lineEnd : _lineEnd - 1; } }

    public void LightSyntaxCheck()
    {
        while (ReadStatementInternal(true) != null) ;
    }

    public string ReadStatement()
    {
        for (string s = ReadStatementInternal(false); s != null; s = ReadStatementInternal(false))
        {
            // skip empty
            for (int i = 0; i < s.Length; i++)
            {
                switch (s[i])
                {
                    case ' ': continue;
                    case '\t': continue;
                    case '\r': continue;
                    case '\n': continue;
                    default:
                        return s;
                }
            }
        }
        return null;
    }

    string ReadStatementInternal(bool syntaxCheck)
    {
        if (_isNextChar == false && _sr.EndOfStream)
            return null;

        StringBuilder allLines = new StringBuilder();
        StringBuilder line = new StringBuilder();
        SqlScriptChunkTypes nextChunk = SqlScriptChunkTypes.InstructionOrUnquotedIdentifier;
        SqlScriptChunkTypes currentChunk = SqlScriptChunkTypes.InstructionOrUnquotedIdentifier;
        char ch = '\0';
        int lineCounter = 0;
        int nextLine = 0;
        int currentLine = 0;
        bool nextCharHandled = false;
        bool foundGO;
        int go = 1;

        while (ReadChar(out ch))
        {
            if (nextCharHandled == false)
            {
                currentChunk = nextChunk;
                currentLine = nextLine;

                switch (currentChunk)
                {
                    case SqlScriptChunkTypes.InstructionOrUnquotedIdentifier:

                        if (ch == '[')
                        {
                            currentChunk = nextChunk = SqlScriptChunkTypes.BracketIdentifier;
                            currentLine = nextLine = lineCounter;
                        }
                        else if (ch == '"')
                        {
                            currentChunk = nextChunk = SqlScriptChunkTypes.DblQuotIdentifierOrLiteral;
                            currentLine = nextLine = lineCounter;
                        }
                        else if (ch == '\'')
                        {
                            currentChunk = nextChunk = SqlScriptChunkTypes.QuotIdentifierOrLiteral;
                            currentLine = nextLine = lineCounter;
                        }
                        else if (ch == '-' && (_isNextChar && _nextChar == '-'))
                        {
                            nextCharHandled = true;
                            currentChunk = nextChunk = SqlScriptChunkTypes.CommentLine;
                            currentLine = nextLine = lineCounter;
                        }
                        else if (ch == '/' && (_isNextChar && _nextChar == '*'))
                        {
                            nextCharHandled = true;
                            currentChunk = nextChunk = SqlScriptChunkTypes.CommentMultiline;
                            currentLine = nextLine = lineCounter;
                        }
                        else if (ch == ']')
                        {
                            throw new SqlBadSyntaxException("Incorrect syntax near ']'.", _lineEnd + lineCounter, _filePath);
                        }
                        else if (ch == '*' && (_isNextChar && _nextChar == '/'))
                        {
                            throw new SqlBadSyntaxException("Incorrect syntax near '*'.", _lineEnd + lineCounter, _filePath);
                        }
                        break;

                    case SqlScriptChunkTypes.CommentLine:

                        if (ch == '\r' && (_isNextChar && _nextChar == '\n'))
                        {
                            nextCharHandled = true;
                            currentChunk = nextChunk = SqlScriptChunkTypes.InstructionOrUnquotedIdentifier;
                            currentLine = nextLine = lineCounter;
                        }
                        else if (ch == '\n' || ch == '\r')
                        {
                            currentChunk = nextChunk = SqlScriptChunkTypes.InstructionOrUnquotedIdentifier;
                            currentLine = nextLine = lineCounter;
                        }
                        break;

                    case SqlScriptChunkTypes.CommentMultiline:

                        if (ch == '*' && (_isNextChar && _nextChar == '/'))
                        {
                            nextCharHandled = true;
                            nextChunk = SqlScriptChunkTypes.InstructionOrUnquotedIdentifier;
                            nextLine = lineCounter;
                        }
                        else if (ch == '/' && (_isNextChar && _nextChar == '*'))
                        {
                            throw new SqlBadSyntaxException("Missing end comment mark '*/'.", _lineEnd + currentLine, _filePath);
                        }
                        break;

                    case SqlScriptChunkTypes.BracketIdentifier:

                        if (ch == ']')
                        {
                            nextChunk = SqlScriptChunkTypes.InstructionOrUnquotedIdentifier;
                            nextLine = lineCounter;
                        }
                        break;

                    case SqlScriptChunkTypes.DblQuotIdentifierOrLiteral:

                        if (ch == '"')
                        {
                            if (_isNextChar && _nextChar == '"')
                            {
                                nextCharHandled = true;
                            }
                            else
                            {
                                nextChunk = SqlScriptChunkTypes.InstructionOrUnquotedIdentifier;
                                nextLine = lineCounter;
                            }
                        }
                        break;

                    case SqlScriptChunkTypes.QuotIdentifierOrLiteral:

                        if (ch == '\'')
                        {
                            if (_isNextChar && _nextChar == '\'')
                            {
                                nextCharHandled = true;
                            }
                            else
                            {
                                nextChunk = SqlScriptChunkTypes.InstructionOrUnquotedIdentifier;
                                nextLine = lineCounter;
                            }
                        }
                        break;
                }
            }
            else
                nextCharHandled = false;

            foundGO = false;
            if (currentChunk == SqlScriptChunkTypes.InstructionOrUnquotedIdentifier || go >= 5 || (go == 4 && currentChunk == SqlScriptChunkTypes.CommentLine))
            {
                // go = 0 - break, 1 - begin of the string, 2 - spaces after begin of the string, 3 - G or g, 4 - O or o, 5 - spaces after GO, 6 - line comment after valid GO
                switch (go)
                {
                    case 0:
                        if (ch == '\r' || ch == '\n')
                            go = 1;
                        break;
                    case 1:
                        if (ch == ' ' || ch == '\t')
                            go = 2;
                        else if (ch == 'G' || ch == 'g')
                            go = 3;
                        else if (ch != '\n' && ch != '\r')
                            go = 0;
                        break;
                    case 2:
                        if (ch == 'G' || ch == 'g')
                            go = 3;
                        else if (ch == '\n' || ch == '\r')
                            go = 1;
                        else if (ch != ' ' && ch != '\t')
                            go = 0;
                        break;
                    case 3:
                        if (ch == 'O' || ch == 'o')
                            go = 4;
                        else if (ch == '\n' || ch == '\r')
                            go = 1;
                        else
                            go = 0;
                        break;
                    case 4:
                        if (ch == '\r' && (_isNextChar && _nextChar == '\n'))
                            go = 5;
                        else if (ch == '\n' || ch == '\r')
                            foundGO = true;
                        else if (ch == ' ' || ch == '\t')
                            go = 5;
                        else if (ch == '-' && (_isNextChar && _nextChar == '-'))
                            go = 6;
                        else
                            go = 0;
                        break;
                    case 5:
                        if (ch == '\r' && (_isNextChar && _nextChar == '\n'))
                            go = 5;
                        else if (ch == '\n' || ch == '\r')
                            foundGO = true;
                        else if (ch == '-' && (_isNextChar && _nextChar == '-'))
                            go = 6;
                        else if (ch != ' ' && ch != '\t')
                            throw new SqlBadSyntaxException("Incorrect syntax was encountered while parsing go.", _lineEnd + lineCounter, _filePath);
                        break;
                    case 6:
                        if (ch == '\r' && (_isNextChar && _nextChar == '\n'))
                            go = 6;
                        else if (ch == '\n' || ch == '\r')
                            foundGO = true;
                        break;
                    default:
                        go = 0;
                        break;
                }
            }
            else
                go = 0;

            if (foundGO)
            {
                if (ch == '\r' || ch == '\n')
                {
                    ++lineCounter;
                }
                // clear GO
                string s = line.Append(ch).ToString();
                for (int i = 0; i < s.Length; i++)
                {
                    switch (s[i])
                    {
                        case ' ': continue;
                        case '\t': continue;
                        case '\r': continue;
                        case '\n': continue;
                        default:
                            _lineStart = _lineEnd;
                            _lineEnd += lineCounter;
                            return allLines.Append(s.Substring(0, i)).ToString();
                    }
                }
                return string.Empty;
            }

            // accumulate by string
            if (ch == '\r' && (_isNextChar == false || _nextChar != '\n'))
            {
                ++lineCounter;
                if (syntaxCheck == false)
                    allLines.Append(line.Append('\r').ToString());
                line.Clear();
            }
            else if (ch == '\n')
            {
                ++lineCounter;
                if (syntaxCheck == false)
                    allLines.Append(line.Append('\n').ToString());
                line.Clear();
            }
            else
            {
                if (syntaxCheck == false)
                    line.Append(ch);
            }
        }

        // this is the end of the stream, return it without GO, if GO exists
        switch (currentChunk)
        {
            case SqlScriptChunkTypes.InstructionOrUnquotedIdentifier:
            case SqlScriptChunkTypes.CommentLine:
                break;
            case SqlScriptChunkTypes.CommentMultiline:
                if (nextChunk != SqlScriptChunkTypes.InstructionOrUnquotedIdentifier)
                    throw new SqlBadSyntaxException("Missing end comment mark '*/'.", _lineEnd + currentLine, _filePath);
                break;
            case SqlScriptChunkTypes.BracketIdentifier:
                if (nextChunk != SqlScriptChunkTypes.InstructionOrUnquotedIdentifier)
                    throw new SqlBadSyntaxException("Unclosed quotation mark [.", _lineEnd + currentLine, _filePath);
                break;
            case SqlScriptChunkTypes.DblQuotIdentifierOrLiteral:
                if (nextChunk != SqlScriptChunkTypes.InstructionOrUnquotedIdentifier)
                    throw new SqlBadSyntaxException("Unclosed quotation mark \".", _lineEnd + currentLine, _filePath);
                break;
            case SqlScriptChunkTypes.QuotIdentifierOrLiteral:
                if (nextChunk != SqlScriptChunkTypes.InstructionOrUnquotedIdentifier)
                    throw new SqlBadSyntaxException("Unclosed quotation mark '.", _lineEnd + currentLine, _filePath);
                break;
        }

        if (go >= 4)
        {
            string s = line.ToString();
            for (int i = 0; i < s.Length; i++)
            {
                switch (s[i])
                {
                    case ' ': continue;
                    case '\t': continue;
                    case '\r': continue;
                    case '\n': continue;
                    default:
                        _lineStart = _lineEnd;
                        _lineEnd += lineCounter + 1;
                        return allLines.Append(s.Substring(0, i)).ToString();
                }
            }
        }

        _lineStart = _lineEnd;
        _lineEnd += lineCounter + 1;
        return allLines.Append(line.ToString()).ToString();
    }

    bool ReadChar(out char ch)
    {
        if (_isNextChar)
        {
            ch = _nextChar;
            if (_sr.EndOfStream)
                _isNextChar = false;
            else
                _nextChar = Convert.ToChar(_sr.Read());
            return true;
        }
        else if (_sr.EndOfStream == false)
        {
            ch = Convert.ToChar(_sr.Read());
            if (_sr.EndOfStream == false)
            {
                _isNextChar = true;
                _nextChar = Convert.ToChar(_sr.Read());
            }
            return true;
        }
        else
        {
            ch = '\0';
            return false;
        }
    }

    public static int ExecuteSqlFile(string filePath, SqlConnection connection, Encoding fileEncoding, int commandTimeout)
    {
        int rowsAffected = 0;
        using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
        {
            // Simple syntax check (you can comment out these two lines below)
            new SqlStatementReader(new StreamReader(fs, fileEncoding)).LightSyntaxCheck();
            fs.Seek(0L, SeekOrigin.Begin);

            // Read statements without GO
            SqlStatementReader rd = new SqlStatementReader(new StreamReader(fs, fileEncoding));
            string stmt;
            while ((stmt = rd.ReadStatement()) != null)
            {
                using (SqlCommand cmd = connection.CreateCommand())
                {
                    cmd.CommandText = stmt;
                    cmd.CommandTimeout = commandTimeout;
                    int i = cmd.ExecuteNonQuery();
                    if (i > 0)
                        rowsAffected += i;
                }
            }
        }
        return rowsAffected;
    }
}
Yargo
quelle
0

Ich hatte das gleiche Problem in Java und habe es mit ein bisschen Logik und Regex gelöst. Ich glaube, dass die gleiche Logik angewendet werden kann. Zuerst lese ich aus der slq-Datei in den Speicher. Dann wende ich die folgende Logik an. Es ist so ziemlich das, was zuvor gesagt wurde, aber ich glaube, dass die Verwendung von Regex-Wort gebunden sicherer ist als die Erwartung eines neuen Zeilenzeichens.

String pattern = "\\bGO\\b|\\bgo\\b";

String[] splitedSql = sql.split(pattern);
for (String chunk : splitedSql) {
  getJdbcTemplate().update(chunk);
}

Dadurch wird die SQL-Zeichenfolge im Wesentlichen in ein Array von SQL-Zeichenfolgen aufgeteilt. Der reguläre Ausdruck besteht im Wesentlichen darin, vollständige "go" -Wörter entweder in Klein- oder Großbuchstaben zu erkennen. Anschließend führen Sie die verschiedenen Abfragen nacheinander aus.

jbrunodomingues
quelle
1
Achtung: Wie werden Sie das aufteilen? insert into books values ('1478355824', 'An Introduction To Programming in Go (paperback)', 9.00)
Blorgbeard ist am
Guter Punkt :-) In meiner Situation gab es keine Dateneinfügungen. Erstellte nur Tabellen, gespeicherte Prozeduren und Funktionen. Das Wort gebunden war in meinem speziellen Fall nützlicher, weil es sich auch um 'go' in der letzten Zeile kümmerte.
jbrunodomingues
0

Ich bin auf dasselbe Problem gestoßen und habe es schließlich durch einen einfachen String-Ersatz gelöst, wobei das Wort GO durch ein Semikolon (;) ersetzt wurde.

Alles scheint gut zu funktionieren, wenn Skripte mit Inline-Kommentaren, Blockkommentaren und GO-Befehlen ausgeführt werden

public static bool ExecuteExternalScript(string filePath)
{
    using (StreamReader file = new StreamReader(filePath))
    using (SqlConnection conn = new SqlConnection(dbConnStr))
    {
        StringBuilder sql = new StringBuilder();

        string line;
        while ((line = file.ReadLine()) != null)
        {
            // replace GO with semi-colon
            if (line == "GO")
                sql.Append(";");
            // remove inline comments
            else if (line.IndexOf("--") > -1)
                sql.AppendFormat(" {0} ", line.Split(new string[] { "--" }, StringSplitOptions.None)[0]);
            // just the line as it is
            else
                sql.AppendFormat(" {0} ", line);
        }
        conn.Open();

        SqlCommand cmd = new SqlCommand(sql.ToString(), conn);
        cmd.ExecuteNonQuery();
    }

    return true;
}
Morvael
quelle
1
Es funktioniert nicht für DDL-Befehle, die sich in einem eigenen Stapel befinden müssen. ZB create / alter table et al.
Blorgbeard ist
Außerdem scheinen Sie Kommentare ohne Grund zu entfernen. Dies würde beispielsweise alle Zeichenfolgen unterbrechen --, die enthalten .
Blorgbeard ist
Hallo Blorgbeard - SQL Server 2012 scheint DDL-Anweisungen in Ordnung zu verarbeiten. Die Skripte, die ich verwendete, sollten es mir ermöglichen, eine gesamte Datenbankstruktur neu zu erstellen, die aktuelle Struktur zu löschen, Tabellen zu erstellen, Indizes hinzuzufügen usw. Ich dachte; auch eine Charge beendet?
Morvael
Das Entfernen von Kommentaren war auch darauf zurückzuführen, dass dadurch eine einzelne SQL-Zeile erstellt wird. Jedes SQL nach dem Kommentar wird daher auskommentiert. Ich nehme jedoch an, dass eine Zeichenfolge enthalten war - das war kein Kommentar.
Morvael
1
Ah ok, habe es nur nachgeschlagen: "CREATE DEFAULT, CREATE FUNCTION, CREATE PROCEDURE, CREATE RULE, CREATE SCHEMA, CREATE TRIGGER und CREATE VIEW können nicht mit anderen Anweisungen in einem Stapel kombiniert werden. Die Anweisung CREATE muss den Stapel starten. Alle Andere Anweisungen, die in diesem Stapel folgen, werden als Teil der Definition der ersten CREATE-Anweisung interpretiert. Eine Tabelle kann nicht geändert werden, und dann werden die neuen Spalten im selben Stapel referenziert. "
Blorgbeard ist
-1

Für alle, die noch das Problem haben. Sie können offizielles Microsoft SMO verwenden

https://docs.microsoft.com/en-us/sql/relational-databases/server-management-objects-smo/overview-smo?view=sql-server-2017

using (var connection = new SqlConnection(connectionString))
{
  var server = new Server(new ServerConnection(connection));
  server.ConnectionContext.ExecuteNonQuery(sql);
}
Sprot
quelle
Dies fügt nichts über die am häufigsten gewählte, akzeptierte Antwort hinzu, was auch auf SMO hindeutet (veröffentlicht vor 10 Jahren!).
Blorgbeard ist am
-4

Zu schwer :)

Erstellen Sie ein Array von Zeichenfolgen str [] und ersetzen Sie GO durch ", @":

            string[] str ={
                @"
USE master;
",@"


CREATE DATABASE " +con_str_initdir+ @";
",@"
-- Verify the database files and sizes
--SELECT name, size, size*1.0/128 AS [Size in MBs] 
--SELECT name 
--FROM sys.master_files
--WHERE name = N'" + con_str_initdir + @"';
--GO

USE " + con_str_initdir + @";
",@"

SET ANSI_NULLS ON
",@"
SET QUOTED_IDENTIFIER ON
",@"

IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Customers]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[Customers](
    [CustomerID] [int] IDENTITY(1,1) NOT NULL,
    [CustomerName] [nvarchar](50) NULL,
 CONSTRAINT [PK_Customers] PRIMARY KEY CLUSTERED 
(
    [CustomerID] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
END
",@"



SET ANSI_NULLS ON
",@"
SET QUOTED_IDENTIFIER ON
",@"
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[GOODS]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[GOODS](
    [GoodsID] [int] IDENTITY(1,1) NOT NULL,
    [GoodsName] [nvarchar](50) NOT NULL,
    [GoodsPrice] [float] NOT NULL,
 CONSTRAINT [PK_GOODS] PRIMARY KEY CLUSTERED 
(
    [GoodsID] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
END
",@"
SET ANSI_NULLS ON
",@"
SET QUOTED_IDENTIFIER ON
",@"
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Orders]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[Orders](
    [OrderID] [int] IDENTITY(1,1) NOT NULL,
    [CustomerID] [int] NOT NULL,
    [Date] [smalldatetime] NOT NULL,
 CONSTRAINT [PK_Orders] PRIMARY KEY CLUSTERED 
(
    [OrderID] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
END
",@"
SET ANSI_NULLS ON
",@"
SET QUOTED_IDENTIFIER ON
",@"
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[OrderDetails]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[OrderDetails](
    [OrderID] [int] NOT NULL,
    [GoodsID] [int] NOT NULL,
    [Qty] [int] NOT NULL,
    [Price] [float] NOT NULL,
 CONSTRAINT [PK_OrderDetails] PRIMARY KEY CLUSTERED 
(
    [OrderID] ASC,
    [GoodsID] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
END
",@"

SET ANSI_NULLS ON
",@"
SET QUOTED_IDENTIFIER ON
",@"
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[InsertCustomers]') AND type in (N'P', N'PC'))
BEGIN
EXEC dbo.sp_executesql @statement = N'-- =============================================
-- Author:      <Author,,Name>
-- Create date: <Create Date,,>
-- Description: <Description,,>
-- =============================================
create PROCEDURE [dbo].[InsertCustomers]
 @CustomerName nvarchar(50),
 @Identity int OUT
AS
INSERT INTO Customers (CustomerName) VALUES(@CustomerName)
SET @Identity = SCOPE_IDENTITY()

' 
END
",@"
IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[dbo].[FK_Orders_Customers]') AND parent_object_id = OBJECT_ID(N'[dbo].[Orders]'))
ALTER TABLE [dbo].[Orders]  WITH CHECK ADD  CONSTRAINT [FK_Orders_Customers] FOREIGN KEY([CustomerID])
REFERENCES [dbo].[Customers] ([CustomerID])
ON UPDATE CASCADE
",@"
ALTER TABLE [dbo].[Orders] CHECK CONSTRAINT [FK_Orders_Customers]
",@"
IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[dbo].[FK_OrderDetails_GOODS]') AND parent_object_id = OBJECT_ID(N'[dbo].[OrderDetails]'))
ALTER TABLE [dbo].[OrderDetails]  WITH CHECK ADD  CONSTRAINT [FK_OrderDetails_GOODS] FOREIGN KEY([GoodsID])
REFERENCES [dbo].[GOODS] ([GoodsID])
ON UPDATE CASCADE
",@"
ALTER TABLE [dbo].[OrderDetails] CHECK CONSTRAINT [FK_OrderDetails_GOODS]
",@"
IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[dbo].[FK_OrderDetails_Orders]') AND parent_object_id = OBJECT_ID(N'[dbo].[OrderDetails]'))
ALTER TABLE [dbo].[OrderDetails]  WITH CHECK ADD  CONSTRAINT [FK_OrderDetails_Orders] FOREIGN KEY([OrderID])
REFERENCES [dbo].[Orders] ([OrderID])
ON UPDATE CASCADE
ON DELETE CASCADE
",@"
ALTER TABLE [dbo].[OrderDetails] CHECK CONSTRAINT [FK_OrderDetails_Orders]


                "};


            for(int i =0; i<str.Length;i++)     
            {
                myCommand.CommandText=str[i];
                try
                {
                myCommand.ExecuteNonQuery();
                }
                catch (SystemException ee)
                {
                    MessageBox.Show("Error   "+ee.ToString());
                }

            }

Das ist alles, viel Spaß.

grv
quelle