【发布时间】:2010-09-07 15:15:59
【问题描述】:
我需要在 C# 程序中执行大量 SQL 语句(创建一堆表、视图和存储过程)。
这些语句需要用GO语句分隔,但SqlCommand.ExecuteNonQuery()不喜欢GO语句。我的解决方案(我想我会发布以供参考)是在 GO 行上拆分 SQL 字符串,并分别执行每个批处理。
有没有更简单/更好的方法?
【问题讨论】:
标签: c# sql-server
我需要在 C# 程序中执行大量 SQL 语句(创建一堆表、视图和存储过程)。
这些语句需要用GO语句分隔,但SqlCommand.ExecuteNonQuery()不喜欢GO语句。我的解决方案(我想我会发布以供参考)是在 GO 行上拆分 SQL 字符串,并分别执行每个批处理。
有没有更简单/更好的方法?
【问题讨论】:
标签: c# sql-server
您可以在每个语句的末尾使用;,因为它对我有用。
真的不知道有没有什么缺点。
【讨论】:
CREATE FUNCTION。在这种情况下,用; 终止最后一个是行不通的。
对于仍有问题的任何人。你可以使用微软官方 SMO
using (var connection = new SqlConnection(connectionString))
{
var server = new Server(new ServerConnection(connection));
server.ConnectionContext.ExecuteNonQuery(sql);
}
【讨论】:
我看了几遍最后决定用EF implementation
为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();
}
}
}
【讨论】:
为了避免第三方、正则表达式、内存开销和快速处理大型脚本,我创建了自己的基于流的解析器。它
可以用 -- 或 /**/ 识别 cmets
-- some commented text
/*
drop table Users;
GO
*/
可以识别带有 ' 或 "的字符串文字
set @s =
'create table foo(...);
GO
create index ...';
和其他结构,例如
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) { }
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;
}
}
【讨论】:
如果你不想使用 SMO,比如你需要跨平台,你也可以使用 SubText 中的 ScriptSplitter 类。
Here's 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 样式 cmets 有问题,请使用正则表达式删除 cmets:
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
删除单行 cmets 在这里:
https://stackoverflow.com/questions/9842991/regex-to-remove-single-line-sql-comments
【讨论】:
/* Go */的情况?
使用能够理解 GO 分隔符的 SQL Server 管理对象 (SMO)。在此处查看我的博文: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);
}
}
}
如果这对您不起作用,请参阅处理该问题的 Phil Haack 的库:http://haacked.com/archive/2007/11/04/a-library-for-executing-sql-scripts-with-go-separators-and.aspx
【讨论】:
TransactionScope 对象一起使用,您只需要注册与当前环境事务的连接即可。在这里查看我的答案:stackoverflow.com/a/18322938/1268570
SqlConnection.InfoMessage) 在 C# 应用程序中查看结果或将结果保存在 txt 文件中,以了解脚本是否成功执行,因为最近在远程主机上执行 150 mb 脚本文件时使用sqlcmd,55 分钟后,一些行受到此错误的影响,TCP Provider: An existing connection was forcibly closed by the remote host.,communication link failure。 ,不知道影响的行数,但我担心运行数据库生成的脚本文件时出现错误消息。
您可以使用SQL Management Objects 来执行此操作。这些与 Management Studio 用于执行查询的对象相同。我相信Server.ConnectionContext.ExecuteNonQuery() 会满足您的需求。
【讨论】:
我遇到了同样的问题,最终通过简单的字符串替换解决了这个问题,用分号 (;) 替换了单词 GO
在使用内联 cmets、块 cmets 和 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;
}
【讨论】:
-- 的字符串。
使用下面的方法来拆分字符串,并逐批执行
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
}
}
}
【讨论】:
如果您不想使用 SMO(这比下面的解决方案更好,但我想提供一个替代方案...)您可以使用此功能拆分您的查询。
它是:
字符串证明(例如 print 'no go')
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;
}
【讨论】:
我在 java 中遇到了同样的问题,我用一些逻辑和正则表达式解决了它。我相信可以应用相同的逻辑。首先我从 slq 文件中读取到内存中。然后我应用以下逻辑。这几乎是之前所说的,但是我相信使用正则表达式单词绑定比期待新行字符更安全。
String pattern = "\\bGO\\b|\\bgo\\b";
String[] splitedSql = sql.split(pattern);
for (String chunk : splitedSql) {
getJdbcTemplate().update(chunk);
}
这基本上将 sql 字符串拆分为一个 sql 字符串数组。正则表达式基本上是检测小写或大写的完整“go”词。然后依次执行不同的查询。
【讨论】:
insert into books values ('1478355824', 'An Introduction To Programming in Go (paperback)', 9.00)
基于 Blorgbeard 的解决方案。
foreach (var sqlBatch in commandText.Split(new[] { "GO" }, StringSplitOptions.RemoveEmptyEntries))
{
sqlCommand.CommandText = sqlBatch;
sqlCommand.ExecuteNonQuery();
}
【讨论】:
我今天通过将我的 SQL 从文本文件加载到一个字符串中来实现这一点。然后我使用字符串拆分函数将字符串分成单独的命令,然后单独发送到服务器。简单的:)
刚刚意识到您需要在 \nGO 上进行拆分,以防万一字母 GO 出现在您的任何表名等中。我猜我很幸运!
【讨论】:
太难了:)
创建字符串数组 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());
}
}
就是这样,享受吧。
【讨论】:
如果您不想安装 SMO 对象,可以使用 gplex 工具(请参阅this answer)
【讨论】:
如果您不想走 SMO 路线,您可以搜索“GO”并将“;”替换为“;”和你想要的查询。请注意,只会返回最后一个结果集。
【讨论】:
“GO”批处理分隔符关键字实际上是由 SQL Management Studio 自己使用的,因此它知道在哪里终止它正在发送到服务器的批处理,而不是传递给 SQL 服务器。如果您愿意,您甚至可以在 Management Studio 中更改关键字。
【讨论】:
这是我为了解决眼前的问题而拼凑起来的。
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命令在自己的一行,并且不会检测block-cmets,所以这种事情会被拆分,并导致错误:
ExecuteBatchNonQuery(@"
/*
GO
*/", conn);
【讨论】:
string sql - 这就是整个脚本。当我提到“批处理”时,我指的是两个“GO”语句之间的一段 SQL 代码。该代码将GO 添加到脚本的末尾,以便foreach 中的代码在您没有以GO 结束脚本时不会跳过最后一批。因此编写的代码将执行所有 SQL。
StringBuilder sqlBatch。
我也遇到了同样的问题,除了将单个 SQL 操作拆分到单独的文件中,然后依次执行所有这些操作之外,我找不到其他方法。
显然问题不在于 DML 命令列表,它们可以在没有 GO 的情况下执行; DDL 的不同故事(创建、更改、删除...)
【讨论】: