Jalankan skrip SQL besar (dengan perintah GO)

89

Saya perlu menjalankan sejumlah besar pernyataan SQL (membuat banyak tabel, tampilan, dan prosedur tersimpan) dari dalam program C #.

Pernyataan ini perlu dipisahkan dengan GOpernyataan, tetapi SqlCommand.ExecuteNonQuery()tidak seperti GOpernyataan. Solusi saya, yang saya kira akan saya posting untuk referensi, adalah membagi string SQL pada GObaris, dan menjalankan setiap batch secara terpisah.

Apakah ada cara yang lebih mudah / lebih baik?

Blorgbeard keluar
sumber

Jawaban:

107

Gunakan SQL Server Management Objects (SMO) yang memahami pemisah GO. Lihat posting blog saya di sini: http://weblogs.asp.net/jongalloway/Handling-_2200_GO_2200_-Separators-in-SQL-Scripts- 2D00 -the-easy-way

Kode sampel:

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

Jika itu tidak berhasil untuk Anda, lihat perpustakaan Phil Haack yang menangani itu: http://haacked.com/archive/2007/11/04/a-library-for-executing-sql-scripts-with-go-separators -and.aspx

Jon Galloway
sumber
2
Bagaimana ini bisa diintegrasikan dengan transaksi? Kode melontarkan InvalidOperationException saat membuat ServerConnection dengan SqlConnection yang memiliki transaksi tertunda.
benPearce
1
Solusi ini berfungsi, saya hanya ingin menambahkan bahwa jika Anda ingin menggunakan transaksi dengan TransactionScopeobjek, Anda hanya perlu meminta koneksi dengan transaksi ambient saat ini. Periksa jawaban saya di sini: stackoverflow.com/a/18322938/1268570
Jupaol
berfungsi dengan baik, tetapi dapatkah kita gunakan SqlConnection.InfoMessage) untuk melihat hasilnya di aplikasi C # atau menyimpan hasilnya dalam txtfile, hanya untuk mengetahui apakah skrip berhasil dieksekusi, karena baru-baru ini menggunakan sqlcmdketika saya mengeksekusi file skrip 150 mb pada host jarak jauh, setelah 55 menit beberapa baris dilakukan dengan kesalahan ini TCP Provider: An existing connection was forcibly closed by the remote host.,, communication link failure. , tidak ada baris yang terpengaruh yang dapat diketahui, tetapi saya khawatir tentang pesan kesalahan saat menjalankan file skrip yang dihasilkan database.
shaijut
5
Solusi ini menyebabkan kegagalan kode Anda ketika beberapa SQL Dll tidak diinstal di mesin. .NET menggunakan beberapa dll bawaan Windows. Tidak adanya beberapa paket fitur SQL (termasuk Objek Manajemen) dapat mencegah kesalahan seperti 'Microsoft.SqlServer.SqlClrProvider.dll' tidak ditemukan. Memperbaiki itu (ini bukan pekerjaan mudah) kesalahan berikutnya akan menjadi 'Microsoft.SqlServer.BathParser.dll' dll Temukan solusi lain untuk memastikan fleksibilitas untuk aplikasi Anda.
Alexandr Sargsyan
Ini tidak berhasil, terutama jika ada pernyataan prosedur perubahan di sana. Itu hanya mengeluh bahwa itu perlu menjadi pernyataan pertama dalam sebuah batch, yaitu, karena ada pemisah batch tepat sebelum itu, tetapi ini masih menimbulkan kesalahan.
Triynko
35

Inilah yang saya ketuk untuk menyelesaikan masalah langsung saya.

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

Ini membutuhkan perintah GO untuk berada di barisnya sendiri, dan tidak akan mendeteksi blok-komentar, jadi hal semacam ini akan terpecah, dan menyebabkan kesalahan:

ExecuteBatchNonQuery(@"
    /*
    GO
    */", conn);
Blorgbeard keluar
sumber
Sangat menyenangkan bahwa saya dapat dengan mudah menyesuaikan ini ke SqlCe jika diperlukan - kode lain menggunakan kelas dan perintah koneksi Sql.
Blue Toque
Saya ingin menjalankan kode ini dengan skrip SQL dengan beberapa prosedur tersimpan di dalamnya, tetapi saya agak bingung, di mana cara membaca SQL? Apa yang Anda maksud dengan 'batch terakhir' adalah kode SQL? Dan jika demikian, bagaimana Anda menentukan kelompok terakhir, dan bagaimana jika saya ingin menjalankan semua kelompok tidak hanya yang terakhir? Terlalu banyak pertanyaan yang saya tahu, tetapi terima kasih jika Anda punya waktu untuk menjawab.
pengguna1676874
Anda meneruskan SQL ke fungsi sebagai string: string sql- itulah seluruh skrip. Ketika saya merujuk ke "batch", maksud saya sepotong kode SQL antara dua pernyataan "GO". Kode menambahkan a GOdi akhir skrip sehingga kode di dalam foreachtidak akan melewatkan batch terakhir jika Anda tidak mengakhiri skrip dengan GO. Jadi kode seperti yang tertulis akan mengeksekusi semua SQL.
Blorgbeard keluar
Saya membuat metode ekstensi: kelas statis internal SqlCommandHelper {void statis internal ExecuteBatchNonQuery (cmd SqlCommand ini, string sql)
Rob Sedgwick

1
Jika Anda ingin menjadi sedikit lebih efisien, Anda dapat menggunakannya StringBuilder sqlBatch.
Lii

11

Anda dapat menggunakan Objek Manajemen SQL untuk melakukan ini. Ini adalah objek yang sama yang digunakan Management Studio untuk mengeksekusi kueri. Saya yakin Server.ConnectionContext.ExecuteNonQuery()akan melakukan apa yang Anda butuhkan.


6

Kata kunci pemisah batch "GO" sebenarnya digunakan oleh SQL Management Studio itu sendiri, sehingga ia tahu di mana harus menghentikan batch yang dikirim ke server, dan tidak diteruskan ke server SQL. Anda bahkan dapat mengubah kata kunci di Studio Manajemen, jika Anda menginginkannya.


6

Saya melihat ini beberapa kali di akhir memutuskan dengan penerapan EF Sedikit dimodifikasi untukSqlConnection

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

Terima kasih @Filip Cordas. Meskipun ini tidak ditandai sebagai jawaban, ini membantu saya seperti pesona! Kami memiliki sejumlah besar skrip di mana BatchTerminator disebutkan dengan cara berbeda seperti kombinasi huruf besar dan kecil (go, Go, GO dll) dan waktu maksimum memiliki spasi tambahan atau spasi yang menyebabkan masalah besar untuk eksekusi melalui c # ... . Terima kasih !!
DipakRiswadkar

2
@DipakRiswadkar Ya mengunci pertanyaan ini beberapa kali dan tidak ada jawaban yang memenuhi kebutuhan saya, jadi melihat penerapan EF tampak bagus jadi saya memposting jawabannya.
Filip Cordas

Jawaban yang luar biasa, ini bekerja seperti pesona, terima kasih banyak
cuongle

@Really juga harus melaporkannya ke tim Entity Framework. Seperti yang saya katakan, ini hanyalah salinan masa lalu dengan sedikit modifikasi.
Filip Cordas


4

Berdasarkan solusi Blorgbeard.

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

3
baru [] {"GO", "Go", "go"}
Andrew Veriga
2
baru [] {"GO", "Go", "go", "gO"}
Brandon Ward
Berfungsi selama Anda tidak memiliki kegunaan lain untuk dua huruf dalam kode Anda, seperti Pernyataan GOTO atau komentar.
Patrik
3

Jika Anda tidak ingin menggunakan SMO, misalnya karena Anda harus lintas platform, Anda juga dapat menggunakan kelas ScriptSplitter dari SubTeks.

Berikut implementasinya di C # & VB.NET

Pemakaian:

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

Jika Anda memiliki masalah dengan komentar gaya-c multiline, hapus komentar dengan 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

Menghapus komentar satu baris ada di sini:

https://stackoverflow.com/questions/9842991/regex-to-remove-single-line-sql-comments
Stefan Steiger
sumber
apakah kelas ini mempertimbangkan /* Go */kasus?
edgarmtze
@cMinor: Tidak di pemisah, tetapi Anda dapat menghapus banyak komentar dengan regex sebelum Anda memisahkan.
Stefan Steiger
2

Saya juga menghadapi masalah yang sama, dan saya tidak dapat menemukan cara lain selain memisahkan operasi SQL tunggal dalam file terpisah, kemudian menjalankan semuanya secara berurutan.

Jelas masalahnya bukan pada daftar perintah DML, mereka bisa dijalankan tanpa GO di antaranya; berbeda cerita dengan DDL (buat, ubah, jatuhkan ...)

ila
sumber
2

Jika Anda tidak ingin pergi ke rute SMO Anda dapat mencari dan mengganti "GO" untuk ";" dan kueri seperti yang Anda inginkan. Perhatikan bahwa hanya set hasil terakhir akan dikembalikan.

jason saldo
sumber
1
Mereka sedang menjalankan ExecuteNonQuery. Ini jauh lebih mudah.
DaveMorganTexas
3
Menggunakan "GO" akan memungkinkan Anda untuk menentukan variabel yang sama lagi di perintah berikutnya dalam batch. Menempatkan titik koma tidak akan melakukan itu.
DDRider62
2

Saya menyelesaikannya hari ini dengan memuat SQL saya dari file teks ke dalam satu string. Saya kemudian menggunakan fungsi Split string untuk memisahkan string menjadi perintah individu yang kemudian dikirim ke server secara individual. Sederhana :)

Baru saja menyadari bahwa Anda perlu berpisah \ nGO kalau-kalau huruf GO muncul di salah satu nama tabel Anda, dll. Sepertinya saya beruntung di sana!

Andy Dove
sumber
2

Jika Anda tidak ingin menggunakan SMO (yang lebih baik daripada solusi di bawah ini, tetapi saya ingin memberikan alternatif ...) Anda dapat membagi kueri Anda dengan fungsi ini.

Ini:

  • Bukti komentar (contoh --GO atau / * GO * /)
  • Hanya bekerja di baris baru, seperti di SSMS (contoh / * test / * GO berfungsi dan pilih 1 sebagai go not
  • Bukti string (contoh cetak '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
sumber
1

gunakan metode berikut untuk memisahkan string dan menjalankan batch demi batch

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
sumber
1

Untuk menghindari pihak ketiga, regex, overhead memori, dan kerja cepat dengan skrip besar, saya membuat parser berbasis aliran saya sendiri. Itu

  • memeriksa sintaks sebelumnya
  • dapat mengenali komentar dengan - atau / ** /

    -- some commented text
     /*
    drop table Users;
    GO
       */
    
  • dapat mengenali string literal dengan 'atau "

    set @s =
        'create table foo(...);
        GO
        create index ...';
    
  • mempertahankan format LF dan CR
  • mempertahankan blok komentar di badan objek (prosedur tersimpan, tampilan, dll.)
  • dan konstruksi lain seperti

          gO -- commented text
    

Cara Penggunaan

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

Pembaca skrip SQL berbasis aliran

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
sumber
0

Saya memiliki masalah yang sama di java dan saya menyelesaikannya dengan sedikit logika dan regex. Saya yakin logika yang sama dapat diterapkan. Pertama saya membaca dari file slq ke dalam memori. Kemudian saya menerapkan logika berikut. Ini cukup banyak apa yang telah dikatakan sebelumnya namun saya percaya bahwa menggunakan ikatan kata regex lebih aman daripada mengharapkan karakter baris baru.

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

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

Ini pada dasarnya membagi string sql menjadi array string sql. Regex pada dasarnya untuk mendeteksi kata-kata 'go' lengkap baik huruf kecil atau besar. Kemudian Anda menjalankan kueri yang berbeda secara berurutan.

jbrunodomingues.dll
sumber
1
Hati-hati: bagaimana Anda akan membagi ini? insert into books values ('1478355824', 'An Introduction To Programming in Go (paperback)', 9.00)
Blorgbeard keluar
Poin bagus :-) Situasi saya tidak memiliki data yang disisipkan. Hanya membuat tabel, prosedur dan fungsi yang tersimpan. Kata terikat lebih berguna dalam kasus khusus saya karena juga menangani 'pergi' di baris terakhir.
jbrunodomingues
0

Saya mengalami masalah yang sama ini dan akhirnya menyelesaikannya dengan mengganti string sederhana, mengganti kata GO dengan titik koma (;)

Semua tampaknya berfungsi dengan baik saat menjalankan skrip dengan komentar sebaris, blokir komentar, dan perintah GO

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
sumber
1
Ini tidak akan berfungsi untuk perintah DDL, yang harus berada dalam kelompoknya sendiri. Misalnya buat / ubah tabel dkk
Blorgbeard keluar
Selain itu, Anda sepertinya menghapus komentar tanpa alasan .. Yang akan merusak string apa pun yang berisi --, misalnya.
Blorgbeard keluar
Hai Blorgbeard - SQL Server 2012 tampaknya menangani pernyataan DDL OK. Skrip yang saya gunakan memungkinkan saya untuk membangun kembali seluruh struktur database, menghapus struktur saat ini, membangun tabel, menambahkan indeks, dll. Saya pikir; juga menyelesaikan satu batch?
Morvael
Juga penghapusan komentar karena ini akan menghasilkan satu baris SQL, oleh karena itu SQL apa pun setelah komentar akan dikomentari, tetapi saya mengambil maksud Anda jika ada string yang berisi - itu bukan komentar.
Morvael
1
Ah oke, baru saja mencarinya: "BUAT DEFAULT, BUAT FUNGSI, BUAT PROSEDUR, BUAT ATURAN, BUAT SKEMA, BUAT PEMICU, dan BUAT TAMPILAN tidak dapat digabungkan dengan pernyataan lain dalam satu kelompok. Pernyataan CREATE harus memulai kelompok. Semua Pernyataan lain yang mengikuti kelompok itu akan diinterpretasikan sebagai bagian dari definisi pernyataan CREATE pertama. Tabel tidak dapat diubah dan kemudian kolom baru direferensikan dalam kelompok yang sama. "
Blorgbeard keluar
-1

Bagi siapa saja yang masih mengalami masalah. Anda dapat menggunakan Microsoft SMO resmi

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
sumber
Ini tidak menambahkan apa pun di atas jawaban yang dipilih dan diterima teratas, yang juga menyarankan SMO (diposting 10 tahun yang lalu!).
Blorgbeard keluar
-4

Terlalu sulit :)

Buat larik string str [] menggantikan GO dengan ", @":

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

            }

Itu saja, selamat menikmati.

grv
sumber