대규모 SQL 스크립트 실행 (GO 명령 사용)


89

C # 프로그램 내에서 대량의 SQL 문 (테이블, 뷰 및 저장 프로 시저 생성)을 실행해야합니다.

이러한 문은 문으로 구분해야 GO하지만 문을 SqlCommand.ExecuteNonQuery()좋아하지 않습니다 GO. 참조 용으로 게시 할 것이라고 생각하는 내 솔루션은 SQL 문자열을 GO줄로 분할하고 각 배치를 개별적으로 실행 하는 것이 었습니다 .

더 쉽고 더 나은 방법이 있습니까?

답변:


107

GO 구분 기호를 이해하는 SQL Server 관리 개체 (SMO)를 사용합니다. 여기 내 블로그 게시물을 참조하십시오 : http://weblogs.asp.net/jongalloway/Handling-_2200_GO_2200_-Separators-in-SQL-Scripts- 2D00 -the-easy-way

샘플 코드 :

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

이것이 작동하지 않으면 Phil Haack의 라이브러리를 참조하십시오. http://haacked.com/archive/2007/11/04/a-library-for-executing-sql-scripts-with-go-separators -and.aspx


2
이것이 트랜잭션과 어떻게 통합 될 수 있습니까? 보류중인 트랜잭션이있는 SqlConnection을 사용하여 ServerConnection을 만들 때 코드에서 InvalidOperationException이 발생합니다.
benPearce

1
이 솔루션은 작동 합니다. 객체 와 트랜잭션 을 사용 TransactionScope하려면 현재 앰비언트 트랜잭션과의 연결을 등록하기 만하면됩니다. 여기 내 대답을 확인하십시오 : stackoverflow.com/a/18322938/1268570
Jupaol

훌륭하게 작동하지만 SqlConnection.InfoMessage)를 사용 하여 C # 응용 프로그램 에서 결과를 보거나 결과를 txt파일에 저장할 수 있습니다. 최근에 sqlcmd내가 원격 호스트에서 150MB 스크립트 파일을 실행할 때 55 분 후에 사용했기 때문입니다. 행에이 오류가 발생했습니다. TCP Provider: An existing connection was forcibly closed by the remote host., communication link failure. , 영향을받는 행을 알 수 없지만 데이터베이스 생성 스크립트 파일을 실행하는 동안 오류 메시지가 걱정됩니다.
shaijut

5
이 솔루션은 일부 SQL Dll이 시스템에 설치되지 않은 경우 코드 오류를 일으켰습니다. .NET은 Windows에 내장 된 일부 dll을 사용합니다. 일부 SQL 기능 팩 (관리 개체 포함)이 없으면 'Microsoft.SqlServer.SqlClrProvider.dll'같은 오류를 찾을 수 없습니다. 수정 (쉬운 작업이 아님) 다음 오류는 'Microsoft.SqlServer.BathParser.dll'등입니다. 응용 프로그램의 유연성을 보장하는 다른 솔루션을 찾으십시오.
Alexandr Sargsyan 2016 년

특히 거기에 alter procedure 문이있는 경우에는 작동하지 않습니다. 즉, 바로 앞에 배치 구분 기호가 있기 때문에 배치에서 첫 번째 문이어야한다고 불평하지만 여전히 오류가 발생합니다.
Triynko

35

이것이 바로 내 당면한 문제를 해결하기 위해 함께 두드린 것입니다.

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 명령이 자체 라인에 있어야하며 블록 주석을 감지하지 않으므로 이런 종류의 것이 분할되어 오류가 발생합니다.

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

필요한 경우이를 SqlCe에 쉽게 적용 할 수 있다는 점이 좋습니다. 다른 코드는 Sql 연결 클래스와 명령을 사용합니다.
Blue Toque

여러 저장 프로 시저가 포함 된 SQL 스크립트로이 코드를 실행하고 싶지만 약간 혼란 스럽습니다. SQL을 어디에서 읽습니까? '마지막 배치'를 언급 할 때 SQL 코드를 의미합니까? 그렇다면 마지막 배치를 어떻게 결정하고 마지막 배치뿐만 아니라 모든 배치를 실행하려면 어떻게해야합니까? 알고있는 질문이 너무 많지만 답변 할 시간이 있으면 감사합니다.
user1676874 dec.

SQL을 문자열로 함수에 전달합니다 string sql.-전체 스크립트입니다. "배치"를 언급 할 때 두 "GO"문 사이의 SQL 코드 덩어리를 의미합니다. 이 코드는 GO스크립트 끝에을 추가 foreach하여 스크립트를 GO. 따라서 작성된 코드는 모든 SQL을 실행합니다.
Blorgbeard는

확장 메서드를 만들었습니다. internal static class SqlCommandHelper {internal static void ExecuteBatchNonQuery (this SqlCommand cmd, string sql)
Rob Sedgwick

1
좀 더 효율적이 되려면 StringBuilder sqlBatch대신 사용할 수 있습니다 .
Lii

11

SQL 관리 개체 를 사용하여이를 수행 할 수 있습니다 . 이는 Management Studio가 쿼리를 실행하는 데 사용하는 것과 동일한 개체입니다. 나는 Server.ConnectionContext.ExecuteNonQuery()당신이 필요한 것을 수행 할 것이라고 믿습니다 .


6

"GO"일괄 구분 키워드는 실제로 SQL Management Studio 자체에서 사용되므로 서버로 보내는 일괄 처리를 종료 할 위치를 알고 SQL 서버로 전달되지 않습니다. 원하는 경우 Management Studio에서 키워드를 변경할 수도 있습니다.


6

나는로 결정 끝에 몇 번 이것 좀 봐 EF 구현 A를 위해 수정 비트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();
                }
            }
        }

@Filip Cordas 감사합니다. 대답으로 표시되지는 않았지만 이것이 매력처럼 도움이되었습니다! BatchTerminator가 대문자와 소문자의 조합 (go, Go, GO 등)과 같은 다른 방식으로 언급 된 스크립트가 많았고 최대 시간에 후행 또는 선행 공백이있어 C #을 통한 실행에 큰 문제가 발생했습니다. . 감사합니다 !!
DipakRiswadkar

2
@DipakRiswadkar 예이 질문에 몇 번 잠 겼고 제공된 답변 중 어느 것도 내 요구를 충족하지 못하므로 EF 구현이 좋아 보였으므로 답변을 게시했습니다.
Filip Cordas 2019

굉장 대답은,이 덕분에, 마법처럼 많은 작품
cuongle

@Really는 Entity Framework 팀에도이를보고해야합니다. 내가 말했듯이 이것은 거의 수정되지 않은 과거의 사본입니다.
Filip Cordas 2019


4

Blorgbeard의 솔루션을 기반으로합니다.

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

3
new [] { "GO", "Go", "go"}
Andrew Veriga

2
새 [] { "GO", "이동", "이동", "GO"}
브랜든 워드

GOTO- 문 또는 주석과 같이 코드의 두 글자를 다른 용도로 사용하지 않는 한 작동합니다.
패트릭

3

예를 들어 크로스 플랫폼이 필요하기 때문에 SMO를 사용하지 않으려면 SubText의 ScriptSplitter 클래스를 사용할 수도 있습니다.

다음 은 C # 및 VB.NET의 구현입니다.

용법:

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

여러 줄로 된 c 스타일 주석에 문제가있는 경우 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

한 줄 주석 제거는 다음과 같습니다.

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

이 수업은 /* Go */사건을 고려 합니까?
edgarmtze 2015

@cMinor : 분할 자에는 없지만 분할하기 전에 정규식으로 여러 줄 주석을 제거 할 수 있습니다.
Stefan Steiger

2

나는 또한 같은 문제에 직면했고 다른 방법을 찾을 수 없었습니다. 단일 SQL 연산을 별도의 파일로 분할 한 다음 모두 순차적으로 실행하는 것입니다.

분명히 문제는 DML 명령 목록이 아니라 중간에 GO없이 실행할 수 있습니다. DDL의 다른 이야기 (생성, 변경, 삭제 ...)


2

SMO 경로로 가고 싶지 않다면 "GO"를 검색하여 ";"로 바꿀 수 있습니다. 그리고 당신이 하듯이 쿼리. 따라서 마지막 결과 집합이 반환됩니다.


1
그들은 ExecuteNonQuery를 실행하고 있습니다. 이것은 훨씬 더 쉬운 방법입니다.
DaveMorganTexas

3
"GO"를 사용하면 배치의 다음 명령에서 동일한 변수를 다시 정의 할 수 있습니다. 세미콜론을 넣으면 그렇게되지 않습니다.
DDRider62

2

오늘은 텍스트 파일의 SQL을 하나의 문자열로로드하여이 작업을 수행했습니다. 그런 다음 문자열 분할 기능을 사용하여 문자열을 개별 명령으로 분리 한 다음 개별적으로 서버로 전송했습니다. 단순 :)

테이블 이름 등에 GO 문자가 표시 될 경우를 대비하여 \ nGO에서 분할해야한다는 것을 깨달았습니다. 내가 거기에서 운이 좋았던 것 같아요!


2

SMO를 사용하지 않으려면 (아래 솔루션보다 낫지 만 대안을 제공하고 싶습니다 ...)이 함수로 쿼리를 분할 할 수 있습니다.

그것은:

  • 주석 증명 (예 : --GO 또는 / * GO * /)
  • SSMS에서와 같이 새 줄에서만 작동합니다 (예 : / * test / * GO 작동하고 1을 선택하십시오.
  • 문자열 증명 (예 : print '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

다음 방법을 사용하여 문자열을 분할하고 배치별로 배치를 실행하십시오.

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

타사, 정규식, 메모리 오버 헤드 및 대용량 스크립트로의 빠른 작업을 피하기 위해 고유 한 스트림 기반 파서를 만들었습니다. 그것

  • 전에 구문 확인
  • -또는 / ** /로 주석을 인식 할 수 있습니다.

    -- some commented text
     /*
    drop table Users;
    GO
       */
    
  • '또는 "로 문자열 리터럴을 인식 할 수 있습니다.

    set @s =
        'create table foo(...);
        GO
        create index ...';
    
  • LF 및 CR 형식 유지
  • 객체 본문 (저장 프로 시저, 뷰 등)의 주석 블록을 보존합니다.
  • 그리고 같은 다른 구조

          gO -- commented text
    

사용하는 방법

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

스트림 기반 SQL 스크립트 리더

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

나는 자바에서 같은 문제가 있었고 약간의 논리와 정규식으로 해결했습니다. 동일한 논리가 적용될 수 있다고 생각합니다. 먼저 slq 파일에서 메모리로 읽습니다. 그런 다음 다음 논리를 적용합니다. 이전에 말한 것과 거의 비슷하지만 정규식 단어 바인딩을 사용하는 것이 새 줄 문자를 예상하는 것보다 안전하다고 생각합니다.

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

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

이것은 기본적으로 SQL 문자열을 SQL 문자열 배열로 분할합니다. 정규식은 기본적으로 소문자 또는 대문자의 전체 '이동'단어를 감지하는 것입니다. 그런 다음 다른 쿼리를 순차적으로 실행합니다.


1
주의 : 이것을 어떻게 나눌 것인가? insert into books values ('1478355824', 'An Introduction To Programming in Go (paperback)', 9.00)
Blorgbeard는 2014

좋은 점 :-) 내 상황에는 데이터가 삽입되지 않았습니다. 테이블, 저장 프로 시저 및 함수를 만드는 것뿐이었습니다. bound라는 단어는 마지막 줄에서 'go'도 처리했기 때문에 내 특별한 경우에 더 유용했습니다.
jbrunodomingues

0

나는이 같은 문제를 겪었고 결국 GO라는 단어를 세미콜론 (;)으로 바꾸는 간단한 문자열 교체로 해결했습니다.

인라인 주석, 블록 주석 및 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
자체 배치에 있어야하는 DDL 명령에는 작동하지 않습니다. 예 : 테이블 생성 / 변경 등
Blorgbeard는 2015

또한 이유없이 주석을 제거하는 것 같습니다 --. 예를 들어.
Blorgbeard는 2015

안녕하세요 Blorgbeard-SQL Server 2012는 DDL 문을 정상적으로 처리하는 것 같습니다. 내가 사용한 스크립트는 전체 데이터베이스 구조를 재 구축하고, 현재 구조를 지우고, 테이블을 구축하고, 인덱스를 추가하는 등의 작업이었습니다. 또한 배치 완료?
Morvael 2015-08-03

또한 주석을 제거하면 한 줄의 SQL이 생성되므로 주석 이후의 모든 SQL이 주석 처리되지만 주석이 아닌 문자열이 포함되어 있으면 요점을 취합니다.
Morvael 2015-08-03

1
알겠습니다. "CREATE DEFAULT, CREATE FUNCTION, CREATE PROCEDURE, CREATE RULE, CREATE SCHEMA, CREATE TRIGGER 및 CREATE VIEW 문은 일괄 처리의 다른 문과 결합 할 수 없습니다. CREATE 문은 일괄 처리를 시작해야합니다. 해당 배치 뒤에 오는 다른 문은 첫 번째 CREATE 문의 정의의 일부로 해석됩니다. 테이블을 변경할 수없는 다음 동일한 배치에서 참조하는 새 열입니다. "
Blorgbeard는

-1

여전히 문제가있는 사람을 위해. 공식 Microsoft SMO를 사용할 수 있습니다.

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

이것은 SMO (10 년 전에 게시 됨!)를 제안하는 가장 많이 득표하고 수락 된 답변에 추가되지 않습니다.
Blorgbeard는

-4

너무 어렵다 :)

GO를 ", @"로 대체하는 str [] 문자열 배열을 만듭니다.

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

            }

그게 다야.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.