![](/img/trans.png)
[英]C# split string containing 'GO' keyword into multiple sql statements
[英]Executing SQL batch containing GO statements in C#
我正在嘗試構建一個程序,它在批處理中執行sql語句並進行錯誤處理(因此我沒有使用SMO)。
問題是GO不是SQL的一部分,當使用.NET執行語句時,它最終會出現錯誤(SMO會處理它,但不會指示執行是否失敗)。
string statements = File.ReadAllText("c:\\test.sql");
string[] splitted = statements.split("GO");
使用上面的行不能解決我的問題,因為GO關鍵字也可以進入注釋(我不想從語句中刪除注釋)並且注釋可以進入/ ** /或在兩個破折號之后 -
例如,我不希望解析以下代碼:
/*
GO
*/
(ofc我谷歌搜索它,但那里沒有解決方案)
最簡單的解決方案(也是最強大的)是使用T-SQL解析器。 好消息是你不必寫它,只需添加引用:
Microsoft.Data.Schema.ScriptDom
Microsoft.Data.Schema.ScriptDom.Sql
然后使用代碼:
static void Main(string[] args)
{
string sql = @"
/*
GO
*/
SELECT * FROM [table]
GO
SELECT * FROM [table]
SELECT * FROM [table]
GO
SELECT * FROM [table]";
string[] errors;
var scriptFragment = Parse(sql, SqlVersion.Sql100, true, out errors);
if (errors != null)
{
foreach (string error in errors)
{
Console.WriteLine(error);
return;
}
}
TSqlScript tsqlScriptFragment = scriptFragment as TSqlScript;
if (tsqlScriptFragment == null)
return;
var options = new SqlScriptGeneratorOptions { SqlVersion = SqlVersion.Sql100, KeywordCasing = KeywordCasing.PascalCase };
foreach (TSqlBatch batch in tsqlScriptFragment.Batches)
{
Console.WriteLine("--");
string batchText = ToScript(batch, options);
Console.WriteLine(batchText);
}
}
public static TSqlParser GetParser(SqlVersion level, bool quotedIdentifiers)
{
switch (level)
{
case SqlVersion.Sql80:
return new TSql80Parser(quotedIdentifiers);
case SqlVersion.Sql90:
return new TSql90Parser(quotedIdentifiers);
case SqlVersion.Sql100:
return new TSql100Parser(quotedIdentifiers);
case SqlVersion.SqlAzure:
return new TSqlAzureParser(quotedIdentifiers);
default:
throw new ArgumentOutOfRangeException("level");
}
}
public static IScriptFragment Parse(string sql, SqlVersion level, bool quotedIndentifiers, out string[] errors)
{
errors = null;
if (string.IsNullOrWhiteSpace(sql)) return null;
sql = sql.Trim();
IScriptFragment scriptFragment;
IList<ParseError> errorlist;
using (var sr = new StringReader(sql))
{
scriptFragment = GetParser(level, quotedIndentifiers).Parse(sr, out errorlist);
}
if (errorlist != null && errorlist.Count > 0)
{
errors = errorlist.Select(e => string.Format("Column {0}, Identifier {1}, Line {2}, Offset {3}",
e.Column, e.Identifier, e.Line, e.Offset) +
Environment.NewLine + e.Message).ToArray();
return null;
}
return scriptFragment;
}
public static SqlScriptGenerator GetScripter(SqlScriptGeneratorOptions options)
{
if (options == null) return null;
SqlScriptGenerator generator;
switch (options.SqlVersion)
{
case SqlVersion.Sql80:
generator = new Sql80ScriptGenerator(options);
break;
case SqlVersion.Sql90:
generator = new Sql90ScriptGenerator(options);
break;
case SqlVersion.Sql100:
generator = new Sql100ScriptGenerator(options);
break;
case SqlVersion.SqlAzure:
generator = new SqlAzureScriptGenerator(options);
break;
default:
throw new ArgumentOutOfRangeException();
}
return generator;
}
public static string ToScript(IScriptFragment scriptFragment, SqlScriptGeneratorOptions options)
{
var scripter = GetScripter(options);
if (scripter == null) return string.Empty;
string script;
scripter.GenerateScript(scriptFragment, out script);
return script;
}
添加引用:
Microsoft.SqlServer.Smo
Microsoft.SqlServer.ConnectionInfo
Microsoft.SqlServer.Management.Sdk.Sfc
然后,您可以使用此代碼:
using (SqlConnection connection = new SqlConnection("Server=(local);Database=Sample;Trusted_Connection=True;"))
{
ServerConnection svrConnection = new ServerConnection(connection);
Server server = new Server(svrConnection);
server.ConnectionContext.ExecuteNonQuery(script);
}
CodeFluent運行時數據庫有一個小的sql文件解析器。 它不處理復雜的情況,但例如支持注釋。
using (StatementReader statementReader = new CodeFluent.Runtime.Database.Management.StatementReader("GO", Environment.NewLine, inputStream))
{
Statement statement;
while ((statement = statementReader.Read(StatementReaderOptions.Default)) != null)
{
Console.WriteLine("-- ");
Console.WriteLine(statement.Command);
}
}
或者更簡單
new CodeFluent.Runtime.Database.Management.SqlServer.Database("connection string")
.RunScript("path", StatementReaderOptions.Default);
只有當“GO”站在一條孤獨的線上或用空格時才會拆分,如下所示:
Regex.Split(statements, @"^\s+GO\s+$");
是的,SSMS必須允許你解決問題。 正如你所提到的,它不是sql的一部分。 SSMS使用SMO來完成它的工作,這就是它在那里工作的原因。
正如您的評論所表明的那樣,但問題很混亂,您需要在處理之前刪除所有注釋塊。 如果您不想這樣做,則需要將文件作為流處理並在/*
處開始忽略並在*/
...處停止,並且可能還要--
和\\n|\\r\\n
您還可以使用正則表達式將其拆分(如果您將其作為文本blob讀取,而不是已被行分解):
var text = File.ReadAllText("file.txt")
var cleanedText = Regex.Replace(text, @"/\*.*\*/", "", RegexOptions.Singleline)
var parts = Regex.Split(cleanedText, @"^\s*GO.*$", RegexOptions.Multiline);
for(var part in parts) {
executeBatch(part);
}
// but this is getting ugly
var str = "what /*\n the \n\n GO \n*/heck\nGO\nand then";
var cleanedText = Regex.Replace(str, @"/\*.*\*/", "\n", RegexOptions.Singleline)
var split = Regex.Split(cleanedText, @"^\s*GO.*$", RegexOptions.Multiline);
// == ["what\nheck", "\nand then"]
是的,正如評論所說,你的真正答案是寫一個解析器 。 即使你對評論的看法,你仍然可以在insert
內的STRING中嵌入/*
和*/
。 所以...
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.