Thực thi một tập lệnh SQL lớn (với các lệnh GO)


89

Tôi cần thực thi một tập hợp lớn các câu lệnh SQL (tạo một loạt các bảng, dạng xem và các thủ tục được lưu trữ) từ trong chương trình C #.

Các câu lệnh này cần được phân tách bằng GOcâu lệnh, nhưng SqlCommand.ExecuteNonQuery()không thích GOcâu lệnh. Giải pháp của tôi, mà tôi cho rằng tôi sẽ đăng để tham khảo, là tách chuỗi SQL trên GOcác dòng và thực thi từng lô riêng biệt.

Có cách nào dễ dàng hơn / tốt hơn không?

Câu trả lời:


105

Sử dụng SQL Server Management Objects (SMO) hiểu các dấu phân cách GO. Xem bài đăng trên blog của tôi tại đây: http://weblogs.asp.net/jongalloway/Handling-_2200_GO_2200_-Separators-in-SQL-Scripts- 2D00 -the-easy-way

Mã mẫu:

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

Nếu điều đó không hiệu quả với bạn, hãy xem thư viện của Phil Haack xử lý điều đó: http://haacked.com/archive/2007/11/04/a-library-for-executing-sql-scripts-with-go-separators -and.aspx


2
Làm thế nào điều này có thể được tích hợp với một giao dịch? Mã ném ra một lỗi không hợp lệ khi tạo kết nối máy chủ với SqlConnection có một giao dịch đang chờ xử lý trên đó.
benPearce

1
Giải pháp này hoạt động, tôi chỉ muốn nói thêm rằng nếu bạn muốn sử dụng các giao dịch với một TransactionScopeđối tượng, bạn chỉ cần tranh thủ kết nối với giao dịch xung quanh hiện tại. Kiểm tra câu trả lời của tôi tại đây: stackoverflow.com/a/18322938/1268570
Jupaol

hoạt động tuyệt vời, nhưng chúng tôi có thể sử dụng SqlConnection.InfoMessage) để xem kết quả trong ứng dụng C # hoặc lưu kết quả trong txttệp, chỉ để biết liệu tập lệnh đã được thực thi thành công hay chưa, bởi vì gần đây sqlcmdkhi sử dụng khi tôi thực thi tệp kịch bản 150 mb trên máy chủ từ xa, sau 55 phút một số hàng đã được thực hiện với lỗi này, TCP Provider: An existing connection was forcibly closed by the remote host., communication link failure. , không có hàng nào bị ảnh hưởng có thể được biết, nhưng tôi lo ngại về các thông báo lỗi khi chạy tệp kịch bản do cơ sở dữ liệu tạo ra.
shaijut

5
Các giải pháp này gây ra lỗi mã của bạn khi một số SQL Dlls không được cài đặt trên máy. .NET sử dụng một số dll được tích hợp sẵn trong Windows. Sự vắng mặt của một số gói tính năng SQL (bao gồm Đối tượng quản lý) có thể ngăn chặn lỗi như 'Microsoft.SqlServer.SqlClrProvider.dll' không tìm thấy. Khắc phục nó (nó không phải là công việc dễ dàng) lỗi tiếp theo sẽ là 'Microsoft.SqlServer.BathParser.dll' vv Tìm giải pháp khác để đảm bảo tính linh hoạt cho ứng dụng của bạn.
Alexandr Sargsyan

35

Đây là những gì tôi đã gặp nhau để giải quyết vấn đề trước mắt của tôi.

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

Nó yêu cầu các lệnh GO phải nằm trên dòng riêng của chúng và sẽ không phát hiện ra các nhận xét khối, vì vậy loại điều này sẽ bị phân tách và gây ra lỗi:

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

Thật tuyệt là tôi có thể dễ dàng điều chỉnh nó với SqlCe nếu cần - đoạn mã khác sử dụng các lớp và lệnh kết nối Sql.
Blue Toque

Tôi muốn chạy mã này với một tập lệnh SQL với một số thủ tục được lưu trữ bên trong, nhưng tôi hơi bối rối, nó đọc SQL ở đâu? Khi bạn đề cập đến 'đợt cuối cùng', bạn có nghĩa là mã SQL? Và nếu vậy, bạn sẽ xác định đợt cuối cùng như thế nào, và nếu tôi muốn chạy tất cả các đợt không chỉ đợt cuối cùng thì sao? Tôi biết quá nhiều câu hỏi, nhưng cảm ơn nếu bạn có thời gian trả lời.
user1676874

Bạn truyền SQL cho hàm dưới dạng một chuỗi: string sql- đó là toàn bộ tập lệnh. Khi tôi đề cập đến "lô", tôi muốn nói đến một đoạn mã SQL giữa hai câu lệnh "GO". Mã thêm a GOvào cuối tập lệnh để mã bên trong foreachsẽ không bỏ qua lô cuối cùng nếu bạn không kết thúc tập lệnh của mình bằng a GO. Vì vậy, mã như được viết sẽ thực thi tất cả SQL.
Blorgbeard ra mắt vào

Tôi tạo ra một phương pháp khuyến nông: lớp tĩnh nội SqlCommandHelper {tĩnh nội trống ExecuteBatchNonQuery (SqlCommand cmd này, chuỗi sql)
Rob Sedgwick

1
Nếu bạn muốn hiệu quả hơn một chút, bạn có thể sử dụng StringBuilder sqlBatchthay thế.
Lii

11

Bạn có thể sử dụng Đối tượng quản lý SQL để thực hiện việc này. Đây là những đối tượng tương tự mà Management Studio sử dụng để thực thi các truy vấn. Tôi tin rằng Server.ConnectionContext.ExecuteNonQuery()sẽ thực hiện được những gì bạn cần.


6

Từ khóa phân tách hàng loạt "GO" thực sự được sử dụng bởi chính SQL Management Studio, để nó biết vị trí kết thúc các lô mà nó đang gửi đến máy chủ và nó không được chuyển đến máy chủ SQL. Bạn thậm chí có thể thay đổi từ khóa trong Management Studio, nếu bạn muốn.


5

Tôi xem xét điều này một vài lần cuối cùng quyết định với việc triển khai EF Một chút sửa đổi choSqlConnection

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

Cảm ơn bạn @Filip Cordas. Mặc dù đây không được đánh dấu là một câu trả lời nhưng đã giúp tôi như một cái duyên! Chúng tôi đã có một lượng lớn các tập lệnh trong đó BatchTerminator được đề cập theo các cách khác nhau như kết hợp chữ hoa và chữ thường (đi, đi, đi, v.v.) và thời gian tối đa có dấu cách ở cuối hoặc đầu với nó, điều này gây ra vấn đề lớn cho việc thực thi thông qua c # ... . Cảm ơn bạn !!
DipakRiswadkar

2
@DipakRiswadkar Có đã bị khóa ở câu hỏi này một vài lần và không có câu trả lời nào được cung cấp đáp ứng nhu cầu của tôi, vì vậy, việc triển khai EF có vẻ tốt nên tôi đã đăng câu trả lời.
Filip Cordas

Tuyệt vời câu trả lời, nó hoạt động như một nét duyên dáng, thanks a lot
Cuongle

@Really cũng nên báo cáo nó cho nhóm Entity Framework. Như tôi đã nói, đây chỉ là một bản sao của quá khứ với một chút sửa đổi.
Filip Cordas


4

Dựa trên giải pháp của Blorgbeard.

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

3
mới [] {"GO", "Go", "go"}
Andrew Veriga

1
mới [] {"GO", "Go", "go", "gO"}
Brandon Ward

Hoạt động miễn là bạn không có mục đích sử dụng nào khác đối với hai chữ cái trong mã của mình, chẳng hạn như GOTO-Statements hoặc nhận xét.
Patrik

3

Ví dụ: nếu bạn không muốn sử dụng SMO vì bạn cần phải đa nền tảng, bạn cũng có thể sử dụng lớp ScriptSplitter từ SubText.

Đây là cách triển khai trong C # & VB.NET

Sử dụng:

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

Nếu bạn gặp sự cố với nhận xét kiểu c nhiều dòng, hãy xóa các nhận xét bằng 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

Xóa nhận xét một dòng ở đây:

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

lớp này có xét /* Go */trường hợp không?
edgarmtze

@cMinor: Không có trong bộ tách, nhưng bạn có thể xóa nhận xét nhiều dòng bằng regex trước khi tách.
Stefan Steiger

2

Tôi cũng gặp phải vấn đề tương tự và tôi không thể tìm được cách nào khác ngoài việc tách hoạt động SQL đơn lẻ thành các tệp riêng biệt, sau đó thực thi tất cả chúng theo trình tự.

Rõ ràng vấn đề không nằm ở danh sách các lệnh DML, chúng có thể được thực thi mà không cần GO ở giữa; câu chuyện khác với DDL (tạo, thay đổi, thả ...)


2

Nếu bạn không muốn đi theo con đường SMO, bạn có thể tìm kiếm và thay thế "GO" cho ";" và truy vấn như bạn muốn. Lưu ý rằng tập hợp kết quả cuối cùng sẽ được trả về.


1
Họ đang thực thi ExecuteNonQuery. Đây là cách dễ dàng hơn.
DaveMorganTexas

3
Sử dụng "GO" sẽ cho phép bạn xác định lại các biến tương tự trong lệnh tiếp theo trong lô. Đặt dấu chấm phẩy sẽ không làm được điều đó.
DDRider62

2

Tôi đã hoàn thành điều này hôm nay bằng cách tải SQL của tôi từ một tệp văn bản thành một chuỗi. Sau đó, tôi đã sử dụng chức năng Tách chuỗi để tách chuỗi thành các lệnh riêng lẻ sau đó được gửi đến máy chủ riêng lẻ. Mẫu :)

Chỉ cần nhận ra rằng bạn cần phải tách trên \ nGO đề phòng các chữ cái ĐI xuất hiện trong bất kỳ tên bảng nào của bạn, v.v. Đoán rằng tôi đã may mắn ở đó!


2

Nếu bạn không muốn sử dụng SMO (tốt hơn giải pháp bên dưới, nhưng tôi muốn đưa ra giải pháp thay thế ...), bạn có thể tách truy vấn của mình bằng hàm này.

Nó là:

  • Nhận xét bằng chứng (ví dụ --GO hoặc / * ĐI * /)
  • Chỉ hoạt động trên một dòng mới, giống như trong SSMS (ví dụ / * thử nghiệm / * GO hoạt động và chọn 1 nếu không
  • Bằng chứng chuỗi (ví dụ in '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;
    }

1

sử dụng phương pháp sau để tách chuỗi và thực thi từng đợt

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

Để tránh các bên thứ ba, regex, chi phí bộ nhớ và làm việc nhanh chóng với các tập lệnh lớn, tôi đã tạo trình phân tích cú pháp dựa trên luồng của riêng mình. Nó

  • kiểm tra cú pháp trước
  • có thể nhận ra nhận xét bằng - hoặc / ** /

    -- some commented text
     /*
    drop table Users;
    GO
       */
  • có thể nhận ra các ký tự chuỗi với 'hoặc "

    set @s =
        'create table foo(...);
        GO
        create index ...';
  • giữ nguyên định dạng LF và CR
  • bảo tồn khối nhận xét trong các thân đối tượng (các thủ tục được lưu trữ, các khung nhìn, v.v.)
  • và các công trình khác như

          gO -- commented text

Cách sử dụng

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

Trình đọc tập lệnh SQL dựa trên luồng

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

Tôi đã gặp vấn đề tương tự trong java và tôi đã giải quyết nó bằng một chút logic và regex. Tôi tin rằng logic tương tự có thể được áp dụng. Đầu tiên tôi đọc từ tệp slq vào bộ nhớ. Sau đó, tôi áp dụng logic sau đây. Đó là khá nhiều điều đã được nói trước đây, tuy nhiên tôi tin rằng sử dụng ràng buộc từ regex sẽ an toàn hơn so với việc mong đợi một ký tự dòng mới.

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

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

Về cơ bản, điều này sẽ chia chuỗi sql thành một mảng các chuỗi sql. Về cơ bản, regex là để phát hiện các từ 'go' đầy đủ, cả chữ thường hoặc chữ hoa. Sau đó, bạn thực hiện tuần tự các truy vấn khác nhau.


1
Cẩn thận: bạn sẽ chia nó như thế nào? insert into books values ('1478355824', 'An Introduction To Programming in Go (paperback)', 9.00)
Blorgbeard ra mắt

Điểm tốt :-) Tình huống của tôi không có chèn dữ liệu. Chỉ là tạo bảng, thủ tục lưu trữ và chức năng. Từ ràng buộc hữu ích hơn trong trường hợp cụ thể của tôi vì nó cũng quan tâm đến 'go' ở dòng cuối cùng.
jbrunodomingues

0

Tôi gặp phải vấn đề tương tự và cuối cùng chỉ giải quyết nó bằng một chuỗi thay thế đơn giản, thay thế từ GO bằng dấu chấm phẩy (;)

Tất cả dường như hoạt động tốt trong khi thực hiện các tập lệnh với nhận xét nội dòng, chặn nhận xét và lệnh 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;
}

1
Nó sẽ không hoạt động đối với các lệnh DDL, các lệnh này cần phải nằm trong lô riêng của chúng. Ví dụ: tạo / thay đổi bảng và cộng sự
Blorgbeard ra mắt vào

Ngoài ra, bạn dường như xóa nhận xét mà không có lý do .. --Ví dụ: điều này sẽ phá vỡ bất kỳ chuỗi nào có chứa .
Blorgbeard ra mắt

Xin chào Blorgbeard - SQL Server 2012 dường như đang xử lý các câu lệnh DDL OK. Các tập lệnh tôi đang sử dụng cho phép tôi xây dựng lại toàn bộ cấu trúc cơ sở dữ liệu, xóa cấu trúc hiện tại, xây dựng bảng, thêm chỉ mục, v.v. Tôi nghĩ; cũng đã hoàn thành một lô?
Morvael

Ngoài ra, việc loại bỏ các nhận xét là bởi vì điều này sẽ tạo ra một dòng SQL duy nhất, bất kỳ SQL nào sau nhận xét do đó sẽ được nhận xét, nhưng tôi hiểu ý bạn nếu có một chuỗi chứa - đó không phải là nhận xét.
Morvael

1
À được rồi, vừa tra cứu: "TẠO DEFAULT, TẠO CHỨC NĂNG, TẠO QUY TRÌNH, TẠO RULE, TẠO SCHEMA, TẠO TRIGGER và TẠO VIEW câu lệnh không thể kết hợp với các câu lệnh khác trong một loạt. Câu lệnh CREATE phải bắt đầu lô. Tất cả các câu lệnh khác tiếp theo trong lô đó sẽ được hiểu là một phần của định nghĩa của câu lệnh CREATE đầu tiên. Không thể thay đổi một bảng và sau đó các cột mới được tham chiếu trong cùng một lô. "
Blorgbeard ra mắt

-1

Đối với bất cứ ai vẫn còn gặp vấn đề. Bạn có thể sử dụng Microsoft SMO chính thức

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

Điều này không thêm bất cứ điều gì so với câu trả lời được bình chọn và chấp nhận hàng đầu, điều này cũng gợi ý về SMO (đã đăng 10 năm trước!).
Blorgbeard ra mắt

-4

Quá khó :)

Tạo mảng chuỗi str [] thay GO bằng ", @":

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

            }

Chỉ vậy thôi, hãy tận hưởng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.