Büyük bir SQL komut dosyası yürütün (GO komutlarıyla)


89

Bir C # programı içinden büyük bir SQL deyimi kümesini (bir grup tablo, görünüm ve saklı yordam oluşturma) yürütmem gerekiyor.

Bu ifadelerin ifadelerle ayrılması gerekir GO, ancak ifadelerden SqlCommand.ExecuteNonQuery()hoşlanmaz GO. Referans olarak göndereceğimi düşündüğüm çözümüm, SQL dizesini GOsatırlara bölmek ve her grubu ayrı ayrı yürütmekti.

Daha kolay / daha iyi bir yol var mı?

Yanıtlar:


108

GO ayırıcılarını anlayan SQL Server Yönetim Nesnelerini (SMO) kullanın. Blog yazıma buradan bakın: http://weblogs.asp.net/jongalloway/Handling-_2200_GO_2200_-Separators-in-SQL-Scripts- 2D00 -the-easy-way

Basit kod:

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);
        }
   }
}

Bu sizin için işe yaramazsa, bunu ele alan Phil Haack'ın kütüphanesine bakın: http://haacked.com/archive/2007/11/04/a-library-for-executing-sql-scripts-with-go-separators -ve.aspx


2
Bu bir işlemle nasıl entegre edilebilir? Kod, üzerinde bekleyen bir işlem olan SqlConnection ile ServerConnection oluştururken bir InvalidOperationException oluşturur.
benPearce

1
Bu çözüm işe yarıyor, sadece bir nesne ile işlemleri kullanmak TransactionScopeistiyorsanız, sadece mevcut ambient işlemi ile bağlantıyı kaydetmeniz gerektiğini eklemek istiyorum . Cevabımı burada kontrol edin: stackoverflow.com/a/18322938/1268570
Jupaol

harika çalışıyor, ancak SqlConnection.InfoMessagesonucu C # uygulamasında görmek veya sonucu txtdosyaya kaydetmek için kullanabilir miyiz , sadece komut dosyasının başarıyla yürütüldüğünü bilmek için, çünkü son zamanlarda sqlcmduzak ana bilgisayarda 150 mb komut dosyası çalıştırdığımda, 55 dakika sonra biraz satırlar Bu hata ile gerçekleştirildi, TCP Provider: An existing connection was forcibly closed by the remote host., communication link failure. , etkilenen satırların hiçbiri bilinemiyor, ancak veritabanı tarafından oluşturulan komut dosyası çalıştırılırken oluşan hata mesajları konusunda endişeliyim.
Shaiju T

5
Bu çözümler, bazı SQL Dll'leri makinede kurulu olmadığında kodunuzun hatasına neden oldu. .NET, Windows'ta yerleşik bazı dll'leri kullanır. Bazı SQL özellik paketlerinin olmaması (Yönetim Nesneleri dahil), 'Microsoft.SqlServer.SqlClrProvider.dll' bulunamadı gibi bir hatayı önleyebilir. Bunu düzeltmek (kolay bir iş değil) sonraki hata 'Microsoft.SqlServer.BathParser.dll' vb olacaktır. Uygulamanız için esneklik sağlamak için başka bir çözüm bulun.
Alexandr Sargsyan

Bu, özellikle orada bir değişiklik prosedürü ifadesi varsa işe yaramaz. Sadece bir toplu işteki ilk ifade olması gerektiğinden şikayet ediyor, çünkü hemen önünde bir toplu iş ayırıcısı var, ancak bu yine de bir hata veriyor.
Triynko

36

Acil sorunumu çözmek için bir araya getirdiğim şey buydu.

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();
    }
}

GO komutlarının kendi satırında olmasını gerektirir ve blok yorumlarını algılamaz, bu nedenle bu tür şeyler bölünecek ve bir hataya neden olacaktır:

ExecuteBatchNonQuery(@"
    /*
    GO
    */", conn);

Gerekirse bunu SqlCe'ye kolayca uyarlayabilmem güzel - diğer kod Sql bağlantı sınıflarını ve komutlarını kullanıyor.
Blue Toque

Bu kodu içinde birkaç saklı yordam bulunan bir SQL komut dosyasıyla çalıştırmak istiyorum, ancak biraz kafam karıştı, SQL'i nerede okuyor? 'Son gruptan' bahsettiğinizde SQL kodunu mu kastediyorsunuz? Ve eğer öyleyse, son partiyi nasıl belirlersiniz ve sadece sonuncusu değil tüm grupları çalıştırmak istersem? Bildiğim çok fazla soru var, ama cevaplayacak vaktin varsa teşekkürler.
user1676874

SQL'i işleve bir dizge olarak iletirsiniz: string sql- bu tüm komut dosyasıdır. Bir "toplu iş" den söz ettiğimde, iki "GO" deyimi arasındaki bir yığın SQL kodu demek istiyorum. Kod GO, komut dosyasının sonuna a ekler , böylece komut dosyanızı bir . İle sonlandırmadıysanız içindeki kod foreachson grubu atlamaz GO. Dolayısıyla, yazılan kod tüm SQL'i çalıştıracaktır.
Blorgbeard

Bir uzantı yöntemi oluşturdum: dahili statik sınıf SqlCommandHelper {internal static void ExecuteBatchNonQuery (this SqlCommand cmd, string sql)
Rob Sedgwick

1
Biraz daha verimli olmak istiyorsanız, StringBuilder sqlBatchbunun yerine kullanabilirsiniz .
Lii

11

Bunu gerçekleştirmek için SQL Yönetim Nesnelerini kullanabilirsiniz. Bunlar, Management Studio'nun sorguları yürütmek için kullandığı nesnelerin aynısıdır. Server.ConnectionContext.ExecuteNonQuery()İhtiyacın olanı yapacağına inanıyorum .


6

"GO" toplu ayırıcı anahtar sözcüğü aslında SQL Management Studio'nun kendisi tarafından kullanılır, böylece sunucuya gönderdiği yığınları nerede sonlandıracağını bilir ve SQL sunucusuna aktarılmaz. Hatta dilerseniz Management Studio'daki anahtar kelimeyi bile değiştirebilirsiniz.


6

Buna birkaç kez bakıyorum, EF uygulamasına karar verdim .SqlConnection

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();
                }
            }
        }

Teşekkürler @Filip Cordas. Bu bir cevap olarak işaretlenmemiş olmasına rağmen, bu bana bir cazibe gibi yardımcı oldu! BatchTerminator'dan büyük ve küçük harf kombinasyonları (go, Go, GO vb.) . Teşekkür ederim !!
DipakRiswadkar

2
@DipakRiswadkar Evet, bu soruya birkaç kez kilitlendim ve verilen cevapların hiçbiri ihtiyaçlarımı karşılamadı, bu yüzden EF uygulamasına baktım, bu yüzden cevabı gönderdim.
Filip Cordas

Harika cevap, bir cazibe gibi çalışıyor, çok teşekkürler
cuongle

@Really bunu Entity Framework ekibine de bildirmelidir. Dediğim gibi, bu sadece küçük değişikliklerle geçmişin bir kopyasıydı.
Filip Cordas


4

Blorgbeard'ın çözümüne dayanıyor.

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

3
yeni [] {"GİT", "Git", "git"}
Andrew Veriga

2
yeni [] {"GİT", "Git", "git", "gO"}
Brandon Ward

Kodunuzdaki iki harf için GOTO-İfadeleri veya yorumlar gibi başka bir kullanımınız olmadığı sürece çalışır.
Patrik

3

SMO'yu kullanmak istemiyorsanız, örneğin çapraz platform olmanız gerektiğinden, SubText'ten ScriptSplitter sınıfını da kullanabilirsiniz.

İşte C # & VB.NET'teki uygulama

Kullanım:

    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);
    }

Çok satırlı c tarzı yorumlarla ilgili sorun yaşıyorsanız, normal ifadeli yorumları kaldırın:

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

Tek satırlık yorumları kaldırmak burada:

https://stackoverflow.com/questions/9842991/regex-to-remove-single-line-sql-comments

bu sınıf durumu dikkate alıyor /* Go */mu?
edgarmtze

@cMinor: Ayırıcıda değil, ancak çok satırlı yorumları ayırmadan önce normal ifadeyle kaldırabilirsiniz.
Stefan Steiger

2

Ben de aynı problemle karşılaştım ve tek bir SQL işlemini ayrı dosyalara böldükten sonra hepsini sırayla yürütmekten başka bir yol bulamadım.

Açıkçası sorun DML komutları listelerinde değil, aralarında GO olmadan yürütülebilirler; DDL ile farklı hikaye (oluştur, değiştir, bırak ...)


2

SMO rotasına gitmek istemiyorsanız, "GO" öğesini arayabilir ve değiştirebilirsiniz; " ve sorgu yaptığınız gibi. Soly'nin son sonuç kümesinin döndürüleceğini unutmayın.


1
ExecuteNonQuery çalıştırıyorlar. Bu çok daha kolay bir yoldur.
DaveMorganTexas

3
"GO" nun kullanılması, toplu işteki bir sonraki komutta aynı değişkenleri yeniden tanımlamanıza olanak sağlar. Noktalı virgül koymak bunu yapmaz.
DDRider62

2

Bunu bugün SQL'imi bir metin dosyasından tek bir dizeye yükleyerek başardım. Daha sonra dizeyi ayrı ayrı komutlara ayırmak için dizge Split işlevini kullandım ve bunlar daha sonra sunucuya ayrı ayrı gönderildi. Basit :)

Masa adlarınızdan herhangi birinde GO harflerinin görünmesi durumunda \ nGO'ya bölmeniz gerektiğini anladım. Sanırım orada şanslıydım!


2

SMO kullanmak istemiyorsanız (ki bu aşağıdaki çözümden daha iyidir, ancak bir alternatif vermek istiyorum ...) sorgunuzu bu fonksiyonla bölebilirsiniz.

Bu:

  • Yorum kanıtı (örnek --GO veya / * GO * /)
  • Yalnızca SSMS'de olduğu gibi yeni bir hatta çalışır (örnek / * test / * GO çalışır ve gitmiyorsa 1'i seçin
  • Dize provası (örnek 'devam etme' baskısı)

    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;
    }
    

1

dizeyi bölmek ve toplu işi toplu olarak yürütmek için aşağıdaki yöntemi kullanın

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
        }

    }
}

1

Üçüncü şahıslardan, normal ifadelerden, bellek ek yüklerinden ve büyük komut dosyalarıyla hızlı çalışmadan kaçınmak için kendi akış tabanlı ayrıştırıcımı yarattım. O

  • sözdizimini daha önce denetler
  • yorumları - veya / ** / ile tanıyabilir

    -- some commented text
     /*
    drop table Users;
    GO
       */
    
  • dize değişmezlerini 'veya "ile tanıyabilir

    set @s =
        'create table foo(...);
        GO
        create index ...';
    
  • LF ve CR formatını korur
  • nesne gövdelerinde yorum bloğunu korur (saklanan prosedürler, görünümler vb.)
  • ve gibi diğer yapılar

          gO -- commented text
    

Nasıl kullanılır

    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) { }

Akış tabanlı SQL komut dosyası okuyucu

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;
    }
}

0

Java'da da aynı sorunu yaşadım ve biraz mantık ve regex ile çözdüm. Aynı mantığın uygulanabileceğine inanıyorum. İlk olarak slq dosyasından belleğe okudum. Sonra aşağıdaki mantığı uyguluyorum. Daha önce söylenenlere çok benziyor, ancak regex word bound kullanmanın yeni bir satır karakteri beklemekten daha güvenli olduğuna inanıyorum.

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

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

Bu, temelde sql dizesini bir dizi sql dizesine böler. Normal ifade, temelde, küçük veya büyük harfli tam 'git' sözcüklerini algılamak içindir. Sonra farklı sorguları sırayla yürütürsünüz.


1
Dikkatli olun: Bunu nasıl böleceksiniz? insert into books values ('1478355824', 'An Introduction To Programming in Go (paperback)', 9.00)
Blorgbeard

İyi nokta :-) Durumumda veri ekleri yoktu. Sadece tablolar, depolanmış prosedürler ve işlevler oluşturuyordu. Bağlama kelimesi benim özel durumumda daha kullanışlıydı çünkü son satırda 'git' ile de ilgileniyordu.
jbrunodomingues

0

Aynı soruna çarptım ve sonunda basit bir dize değiştirme ile çözdüm, GO kelimesini noktalı virgül (;) ile değiştirdim.

Satır içi yorumlar, blok yorumları ve GO komutları içeren komut dosyalarını çalıştırırken her şey sorunsuz çalışıyor gibi görünüyor

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;
}

1
Kendi toplu işlerinde olması gereken DDL komutları için çalışmayacaktır. Örneğin, tablo oluşturma / değiştirme ve diğerleri
Blorgbeard

Ayrıca, açıklamaları sebepsiz yere kaldırıyor gibi görünüyorsunuz .. Bu --, örneğin içeren dizeleri bozar .
Blorgbeard

Merhaba Blorgbeard - SQL Server 2012, DDL ifadelerini işliyor gibi görünüyor. Kullandığım betikler, bütün bir veritabanı yapısını yeniden kurmama, mevcut yapıyı silmeme, tablolar oluşturmama, indeksler eklememe vb. ayrıca bir parti bitirdiniz mi?
Morvael

Ayrıca yorumların kaldırılması tek bir SQL satırı üreteceği için, yorumdan sonraki herhangi bir SQL bu nedenle yorumlanacaktı, ancak içeren bir dize varsa - bu bir yorum değildi.
Morvael

1
Ah tamam, şimdi baktım: "CREATE DEFAULT, CREATE FUNCTION, CREATE PROCEDURE, CREATE RULE, CREATE SCHEMA, CREATE TRIGGER ve CREATE VIEW deyimleri bir toplu işteki diğer ifadelerle birleştirilemez. CREATE deyimi toplu işi başlatmalıdır. bu toplu işte takip eden diğer ifadeler, ilk CREATE deyiminin tanımının bir parçası olarak yorumlanacaktır. Bir tablo değiştirilemez ve ardından aynı toplu işte yeni sütunlara başvurulamaz. "
Blorgbeard

-1

Hala sorunu olan herkes için. Resmi Microsoft SMO'yu kullanabilirsiniz

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);
}

Bu, en çok oylanan, kabul edilen cevaba hiçbir şey eklemiyor, bu da SMO'yu (10 yıl önce yayınlandı!)
Blorgbeard

-4

Çok zor :)

GO 'yu ", @" ile değiştirerek [] dize dizisi oluşturun:

            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());
                }

            }

Hepsi bu, tadını çıkarın.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.