Выполнить большой скрипт SQL (с помощью команд GO)
Мне нужно выполнить большой набор операторов SQL (создание набора таблиц, представлений и хранимых процедур) из программы на C#.
Эти заявления должны быть разделены GO
заявления, но SqlCommand.ExecuteNonQuery()
не любит GO
заявления. Мое решение, которое я опубликую для справки, было разделить строку SQL на GO
линии, и выполнить каждую партию отдельно.
Есть ли более простой / лучший способ?
20 ответов
Используйте объекты управления SQL Server (SMO), которые понимают разделители GO. Смотрите мой блог здесь: http://weblogs.asp.net/jgalloway/archive/2006/11/07/Handling-_2200_GO_2200_-Separators-in-SQL-Scripts-_2D00_-the-easy-way.aspx
Образец кода:
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
Это то, что я собрал вместе, чтобы решить мою непосредственную проблему.
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);
Вы можете использовать объекты управления SQL для этого. Это те же объекты, которые Management Studio использует для выполнения запросов. я верю Server.ConnectionContext.ExecuteNonQuery()
будет выполнять то, что вам нужно.
Я смотрю на это несколько раз в конце решил с реализацией EF Немного модифицированный для 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();
}
}
}
Ключевое слово "GO" для разделения пакетов фактически используется самой SQL Management Studio, поэтому оно знает, где завершить пакеты, отправляемые на сервер, и не передается на сервер SQL. Вы можете даже изменить ключевое слово в Management Studio, если пожелаете.
Если вы не хотите устанавливать объекты SMO, вы можете использовать инструмент gplex (см. Этот ответ)
Основано на решении Blorgbeard.
foreach (var sqlBatch in commandText.Split(new[] { "GO" }, StringSplitOptions.RemoveEmptyEntries))
{
sqlCommand.CommandText = sqlBatch;
sqlCommand.ExecuteNonQuery();
}
Если вы не хотите использовать 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://stackru.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://stackru.com/questions/9842991/regex-to-remove-single-line-sql-comments
Если вы не хотите использовать SMO (что лучше, чем приведенное ниже решение, но я хочу дать альтернативу...), вы можете разделить ваш запрос с помощью этой функции.
Это:
- Доказательство комментария (пример --GO или /* GO */)
- Работает только на новой строке, так же, как в SSMS (пример /* test /* GO работает и выберите 1 как go not
Строковое доказательство (пример печати "не ходи")
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; }
Если вы не хотите идти по маршруту SMO, вы можете найти и заменить "GO" на ";" и запрос, как вы бы. Обратите внимание, что будет возвращен последний результат.
Я сделал это сегодня, загрузив мой SQL из текстового файла в одну строку. Затем я использовал функцию разделения строк, чтобы разделить строку на отдельные команды, которые затем отправлялись на сервер по отдельности. Просто:)
Просто понял, что вам нужно разделить \nGO на тот случай, если буквы GO появятся в любом из имен ваших таблиц и т. Д. Думаю, мне повезло!
Я также столкнулся с той же проблемой, и я не мог найти другого пути, кроме как разбить одну операцию SQL на отдельные файлы, а затем выполнить их все по порядку.
Очевидно, что проблема не в списках команд DML, они могут выполняться без GO; другая история с DDL (создание, изменение, удаление...)
Используйте следующий метод, чтобы разбить строку и выполнить пакет за партией
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
}
}
}
Чтобы избежать посторонних, регулярных выражений, накладных расходов памяти и быстрой работы с большими скриптами, я создал свой собственный потоковый парсер. Это
- проверяет синтаксис перед
можно распознать комментарии с помощью - или /**/
-- 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;
}
}
У меня была та же проблема в Java, и я решил ее с небольшим количеством логики и регулярных выражений. Я полагаю, что та же самая логика может быть применена. Сначала я читаю из файла slq в память. Затем я применяю следующую логику. Это в значительной степени то, что было сказано ранее, однако я считаю, что использование регулярных выражений более безопасно, чем ожидание нового символа строки.
String pattern = "\\bGO\\b|\\bgo\\b";
String[] splitedSql = sql.split(pattern);
for (String chunk : splitedSql) {
getJdbcTemplate().update(chunk);
}
Это в основном разбивает строку SQL в массив строк SQL. Регулярное выражение в основном для обнаружения полных "ходовых" слов в нижнем или верхнем регистре. Затем вы выполняете различные запросы последовательно.
You can just use
;
at the end of each statement as it worked for me. Really don't know if there are any drawbacks to it.
Вот самое элегантное решение, которое я смог найти.
Скажем, ваш SQL-скрипт, содержащий
GO
операторы находятся в файле
script.sql
.
Ты можешь сделать:
$query = ((Get-Content -Raw "script.sql") -replace '([\s\n]*)GO([\s\n]+)','$1$2')
Это удалит все операторы GO без удаления других вхождений строки.
go
где-нибудь еще (согласно моим тестам).
Затем вы можете сделать:
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlConnection.ConnectionString = <your_connection_string>
$SqlConnection.Open()
$SqlCmd.Connection = $SqlConnection
$SqlCmd.CommandText = $query
$SqlCmd.ExecuteNonQuery()
который должен нормально работать без проблем.
Я столкнулся с той же проблемой и в итоге просто решил ее простой заменой строки, заменив слово 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;
}
Для тех, кто все еще имеет проблемы. Вы можете использовать официальный Microsoft SMO
using (var connection = new SqlConnection(connectionString))
{
var server = new Server(new ServerConnection(connection));
server.ConnectionContext.ExecuteNonQuery(sql);
}
Слишком трудно:)
Создайте массив строк str[], заменив GO на ",@":
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());
}
}
Вот и все, наслаждайся.