C# 在C中执行包含GO语句的SQL批处理#

C# 在C中执行包含GO语句的SQL批处理#,c#,sql,.net,sql-server,C#,Sql,.net,Sql Server,我正在尝试构建一个程序,该程序在批处理错误的情况下执行sql语句 (因此,我没有使用SMO) 问题在于GO不是SQL的一部分,当使用.NET执行语句时,它最终会出错(SMO会处理它,但不会给出执行是否失败的任何指示) 使用以上几行并不能解决我的问题,因为GO关键字也可以放在注释中(我不想从语句中删除注释),注释也可以放在注释中 /**/ 或者在两个破折号之后-- 例如,我不希望解析以下代码: /* GO */ (ofc我在谷歌上搜索了一下,但那里没有解决方案)只有当“GO”站在一条孤零零的线上

我正在尝试构建一个程序,该程序在批处理错误的情况下执行sql语句 (因此,我没有使用SMO)

问题在于GO不是SQL的一部分,当使用.NET执行语句时,它最终会出错(SMO会处理它,但不会给出执行是否失败的任何指示)

使用以上几行并不能解决我的问题,因为GO关键字也可以放在注释中(我不想从语句中删除注释),注释也可以放在注释中 /**/ 或者在两个破折号之后--
例如,我不希望解析以下代码:

/*
GO
*/
(ofc我在谷歌上搜索了一下,但那里没有解决方案)

只有当“GO”站在一条孤零零的线上或带有空格时才拆分,如下所示:

Regex.Split(statements, @"^\s+GO\s+$");

是的,围棋是SSMS必须允许你打破的东西。正如您所提到的,它不是sql的一部分。SSMS使用SMO来完成它的工作,这就是它在那里工作的原因

正如您的评论所表明的,但问题是模糊的,您需要在处理之前删除所有评论块。如果不想这样做,则需要将文件作为流进行处理,并在
/*
处开始忽略,在
*/
处停止。。。也可能是
--
\n\r\n

您还可以使用正则表达式将其拆分(如果您将其作为文本块读入,而不是以行分隔):

是的,正如评论所说,您真正的答案是编写解析器。即使您对注释有什么看法,您仍然可以将
/*
*/
嵌入到
插入
中的字符串中。所以

脚本世界 最简单的解决方案(也是最健壮的)是使用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;
}
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语句都在一个新行上吗?链接问题的答案不能解决GO出现在内部的情况,例如:/*GO/第一行是/第二行是GO,第三行是*/我明白了,那么您需要一个更复杂的算法。解析和所有的麻烦。您应该根据所需的复杂性权衡您的选项(和时间限制)。您还忽略了
GO
很容易出现在字符串中的事实。忘记了继续。另外,我可能会在某个时候将内部执行批处理和“最后一行”的测试打包成一行。这是一个丑陋的密码:DHow不是。我已经编写了几十次这样的代码来处理包含数十万条insert语句的文件。也许你可以澄清你的问题?为了澄清,如果语句包含在第一行/*第二行GO和第三行*/上面的答案会试图拆分语句正如我告诉你的,Nadav,这个问题需要至少使用一个15/20行递归函数进行实际解析。尝试实现它,如果您有任何问题,请在另一个问题中发布您的代码。就目前而言,已经给出的答案是快速修复,真正的答案是:不可能,编写一个解析器。然后需要预解析文件以删除注释块。但是您正在进入一个非常不标准的领域,/**/中出现的.GO语句仍然会导致它被拆分,并且错误很快就会出现it@NadavStern是的,这个问题将一直存在于所有基于字符串操作的解决方案中。所以这里所有的答案都是一样的。要解决最后一个问题,您必须解释文本并生成语句、字符串和注释的实际语义。您将需要一个真正的解析器,可能是递归的,因此您会发现它对于这样的任务是不可行的。我看到的唯一解决方案是按约定更改输入:强制用户编写单独的文件,而不是使用
GO
.Regex.Split(语句@“^\s*GO\s*$”,RegexOptions.Multiline | RegexOptions.IgnoreCase)对我来说很好。但请再快速查看一下您的switch语句<代码>Sql80=>
100
100
=>
80
?是这样吗?谢谢你报告这个错误。我解决了,这是一个很酷的解决方案。我以前可能试过,它看起来很熟悉。可能没有使用它,因为我生成的文件是正常的(例如生成的SSM)。对于其他人,您需要安装什么才能使这些库可用,它们是否在GAC中,等等。库位于GAC中,或者可以在“C:\Program files(x86)\Microsoft Visual Studio 10.0\VSTSDB”中找到。应该运行的脚本可以包含存储过程,将alter与此解决方案一起使用将导致从存储过程中删除所有注释
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);
}
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);