Execute a large SQL script (with GO commands)
Solution 1:
Use SQL Server Management Objects (SMO) which understands GO separators. See my blog post here: http://weblogs.asp.net/jongalloway/Handling-_2200_GO_2200_-Separators-in-SQL-Scripts-2D00-the-easy-way
Sample code:
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);
}
}
}
If that won't work for you, see Phil Haack's library which handles that: http://haacked.com/archive/2007/11/04/a-library-for-executing-sql-scripts-with-go-separators-and.aspx
Solution 2:
This is what I knocked together to solve my immediate problem.
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();
}
}
It requires GO commands to be on their own line, and will not detect block-comments, so this sort of thing will get split, and cause an error:
ExecuteBatchNonQuery(@"
/*
GO
*/", conn);
Solution 3:
You can use SQL Management Objects to perform this. These are the same objects that Management Studio uses to execute queries. I believe Server.ConnectionContext.ExecuteNonQuery()
will perform what you need.
Solution 4:
I look at this a few times at the end decided with EF implementation
A bit modified for 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();
}
}
}