我正在构建一个自定义数据库部署实用程序,我需要读取包含sql脚本的文本文件并对数据库执行它们。
非常简单的东西,到目前为止一直很好。
但是我遇到了一个障碍,文件的内容被成功完整地读取,但是一旦传入SqlCommand然后用SqlCommand.ExecuteNonQuery执行,只执行部分脚本。
我启动了Profiler并确认我的代码没有传递所有脚本。
private void ExecuteScript(string cmd, SqlConnection sqlConn, SqlTransaction trans)
{
SqlCommand sqlCmd = new SqlCommand(cmd, sqlConn, trans);
sqlCmd.CommandType = CommandType.Text;
sqlCmd.CommandTimeout = 9000000; // for testing
sqlCmd.ExecuteNonQuery();
}
// I call it like this, readDMLScript contains 543 lines of T-SQL
string readDMLScript = ReadFile(dmlFile);
ExecuteScript(readDMLScript, sqlConn, trans);
答案 0 :(得分:35)
是的,每当他们第一次开始将SQL脚本文件的内容发送到数据库时,每个人都会遇到这个障碍。
GO
不是T-SQL命令。它是所有Microsoft交互式SQL工具(Management Studio,isql,osql)都认可的批处理结束标记。为了处理它,你必须编写自己的解析器来打破GO
语句之间文件中的每个文本块,并将它们作为单独的命令提供给数据库。
如何实施解析器取决于您。它可以很简单(一次读取每一行,检测除GO
和空格之外的任何内容的行)或复杂(标记所有语句并确定GO
是否是真实的陈述或字符串或多行注释中的一些文本。)
就个人而言,我选择了第一个选项。它可以处理您可能遇到的所有SQL文件的99%而不用大惊小怪。如果你想要全力以赴并写一个代币,我相信很多人已经做了一个,只有谷歌。
示例:
using(var reader = new SqlBatchReader(new StreamReader(dmlFile))) {
string batch;
while((batch = reader.ReadBatch()) != null) {
var cmd = new SqlCommand(batch, conn, trans) { CommandType = CommandType.Text };
cmd.ExecuteNonQuery();
}
}
class SqlBatchReader : IDisposable {
private TextReader _reader;
public SqlBatchReader(TextReader reader) {
_reader = reader;
}
/// <summary>
/// Return the next command batch in the file, or null if end-of-file reached.
/// </summary>
public string ReadBatch() {
// TODO: Implement your parsing logic here.
}
}
答案 1 :(得分:5)
我在搜索此问题的答案时找到了以下代码:
优点:简单易懂,完全符合我的需求。
缺点:它比基于Stream的解决方案效率低,并且区分大小写(即“GO”不是“go”)。
string[] commands = sql.Split(new string[]{"GO\r\n", "GO ", "GO\t"}, StringSplitOptions.RemoveEmptyEntries );
foreach (string c in commands)
{
var command = new SqlCommand(c, masterConnection);
command.ExecuteNonQuery();
}
答案 2 :(得分:2)
SMO中的ExecuteNonQuery适用于批处理:
答案 3 :(得分:1)
根据原帖后的评论回答:
GO是Management Studio / osql / isql的标记。它告诉您向SQL Server发送一批命令。在您的实用程序中,您应该使用GO作为分隔符拆分输入数据并单独发送每个元素(不使用GO命令)
答案 4 :(得分:0)
这是我们使用的:))
public static class ExtensionMethodsSqlCommand
{
#region Public
private static bool IsGo(string psCommandLine)
{
if (psCommandLine == null)
return false;
psCommandLine = psCommandLine.Trim();
if (string.Compare(psCommandLine, "GO", StringComparison.OrdinalIgnoreCase) == 0)
return true;
if (psCommandLine.StartsWith("GO", StringComparison.OrdinalIgnoreCase))
{
psCommandLine = (psCommandLine + "--").Substring(2).Trim();
if (psCommandLine.StartsWith("--"))
return true;
}
return false;
}
[System.Diagnostics.DebuggerHidden]
public static void ExecuteNonQueryWithGos(this SqlCommand poSqlCommand)
{
string sCommandLong = poSqlCommand.CommandText;
using (StringReader oStringReader = new StringReader(sCommandLong))
{
string sCommandLine;
string sCommandShort = string.Empty;
while ((sCommandLine = oStringReader.ReadLine()) != null)
if (ExtensionMethodsSqlCommand.IsGo(sCommandLine))
{
if (sCommandShort.IsNullOrWhiteSpace() == false)
{
if ((poSqlCommand.Connection.State & ConnectionState.Open) == 0)
poSqlCommand.Connection.Open();
using (SqlCommand oSqlCommand = new SqlCommand(sCommandShort, poSqlCommand.Connection))
oSqlCommand.ExecuteNonQuery();
}
sCommandShort = string.Empty;
}
else
sCommandShort += sCommandLine + "\r\n";
}
}
#endregion Public
}
答案 5 :(得分:0)
我最终编写了一个StringReader的实现来执行此操作。
它处理:
因此,当用作批处理分隔符时,它只会检测关键字GO。这意味着它可以正确地拆分SQL文本。
如果你将一个sql终止符(分号)附加到单词GO
,它也会处理您可以找到它的代码here:
你这样使用它:
using (var reader = new SqlCommandReader(scriptContents))
{
var commands = new List<string>();
reader.ReadAllCommands(c => commands.Add(c));
// commands now contains each seperated sql batch.
}