Выполнить большой скрипт SQL (с помощью команд GO) - PullRequest
81 голосов
/ 03 сентября 2008

Мне нужно выполнить большой набор операторов SQL (создание группы таблиц, представлений и хранимых процедур) из программы на C #.

Эти операторы должны быть разделены GO операторами, но SqlCommand.ExecuteNonQuery() не любит GO операторов. Мое решение, которое я опубликую для справки, заключалось в разделении строки SQL на строки GO и выполнении каждого пакета отдельно.

Есть ли более простой / лучший способ?

Ответы [ 18 ]

99 голосов
/ 03 сентября 2008

Использовать объекты управления SQL Server (SMO), которые понимают разделители GO. Смотрите мой блог здесь: 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);
        }
   }
}

Если это не сработает для вас, посмотрите библиотеку Фила Хаака, которая обрабатывает это: http://haacked.com/archive/2007/11/04/a-library-for-executing-sql-scripts-with-go-separators-and.aspx

31 голосов
/ 03 сентября 2008

Это то, что я собрал вместе, чтобы решить мою непосредственную проблему.

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);
9 голосов
/ 03 сентября 2008

Для этого можно использовать Объекты управления SQL . Это те же объекты, которые Management Studio использует для выполнения запросов. Я верю, что Server.ConnectionContext.ExecuteNonQuery() выполнит то, что вам нужно.

6 голосов
/ 03 сентября 2008

Ключевое слово «GO» для разделения пакетов фактически используется самой SQL Management Studio, поэтому оно знает, где завершить пакеты, отправляемые на сервер, и не передается на сервер SQL. Вы можете даже изменить ключевое слово в Management Studio, если хотите.

4 голосов
/ 28 мая 2014

Основано на решении Blorgbeard.

foreach (var sqlBatch in commandText.Split(new[] { "GO" }, StringSplitOptions.RemoveEmptyEntries))
{
   sqlCommand.CommandText = sqlBatch;
   sqlCommand.ExecuteNonQuery();
}
3 голосов
/ 23 сентября 2014

Если вы не хотите использовать SMO, например, потому что вам нужно быть кроссплатформенным, вы также можете использовать класс ScriptSplitter из SubText.

Вот реализация в 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, удалите комментарии с помощью регулярного выражения:

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
3 голосов
/ 27 марта 2012

Если вы не хотите устанавливать объекты SMO, вы можете использовать инструмент gplex (см. этот ответ )

2 голосов
/ 26 ноября 2012

Я выполнил это сегодня, загрузив свой SQL из текстового файла в одну строку. Затем я использовал функцию разделения строк, чтобы разделить строку на отдельные команды, которые затем отправлялись на сервер по отдельности. Simples:)

Просто понял, что вам нужно разделить \ nGO на тот случай, если буквы GO появятся в любом из имен ваших таблиц и т. Д. Думаю, мне повезло!

2 голосов
/ 03 сентября 2008

Если вы не хотите идти по маршруту SMO, вы можете найти и заменить «GO» на «;» и запрос, как вы бы. Обратите внимание, что будет возвращен последний набор результатов.

2 голосов
/ 03 сентября 2008

Я также столкнулся с той же проблемой, и я не мог найти другого пути, кроме как разбить одну операцию SQL на отдельные файлы, а затем выполнить их все по порядку.

Очевидно, что проблема не в списках команд DML, они могут выполняться без GO между ними; другая история с DDL (создание, изменение, удаление ...)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...