39

我有一个非常简单的 C# 命令外壳应用程序,它执行由 SQL Server 生成的用于编写模式和数据的 sql 脚本。它在“GO”声明中爆炸。错误信息:

'GO' 附近的语法不正确。

这是完整的 sql 脚本:

/****** Object:  Table [gym].[MembershipStatus]    Script Date: 9/3/2013 9:24:01 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [gym].[MembershipStatus](
    [MembershipStatusID] [tinyint] IDENTITY(1,1) NOT NULL,
    [Name] [varchar](75) NOT NULL,
    [Description] [varchar](400) NOT NULL,
    [AllowCheckin] [bit] NOT NULL,
    [IncludeInCollections] [bit] NOT NULL,
    [ScheduleFutureInvoices] [bit] NOT NULL,
 CONSTRAINT [MembershipStatus_PK] PRIMARY KEY CLUSTERED 
(
    [MembershipStatusID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO
SET ANSI_PADDING OFF
GO
SET IDENTITY_INSERT [gym].[MembershipStatus] ON 

INSERT [gym].[MembershipStatus] ([MembershipStatusID], [Name], [Description], [AllowCheckin], [IncludeInCollections], [ScheduleFutureInvoices]) VALUES (1, N'Active', N'Active', 1, 1, 1)
INSERT [gym].[MembershipStatus] ([MembershipStatusID], [Name], [Description], [AllowCheckin], [IncludeInCollections], [ScheduleFutureInvoices]) VALUES (2, N'Cancelled', N'Cancelled', 0, 1, 0)
INSERT [gym].[MembershipStatus] ([MembershipStatusID], [Name], [Description], [AllowCheckin], [IncludeInCollections], [ScheduleFutureInvoices]) VALUES (3, N'Collection', N'Collection', 0, 0, 0)
INSERT [gym].[MembershipStatus] ([MembershipStatusID], [Name], [Description], [AllowCheckin], [IncludeInCollections], [ScheduleFutureInvoices]) VALUES (4, N'Deleted', N'Deleted', 0, 0, 0)
INSERT [gym].[MembershipStatus] ([MembershipStatusID], [Name], [Description], [AllowCheckin], [IncludeInCollections], [ScheduleFutureInvoices]) VALUES (5, N'Expired', N'Expired', 1, 1, 1)
INSERT [gym].[MembershipStatus] ([MembershipStatusID], [Name], [Description], [AllowCheckin], [IncludeInCollections], [ScheduleFutureInvoices]) VALUES (6, N'Freeze', N'Freeze', 0, 1, 0)
INSERT [gym].[MembershipStatus] ([MembershipStatusID], [Name], [Description], [AllowCheckin], [IncludeInCollections], [ScheduleFutureInvoices]) VALUES (7, N'Inactive', N'Inactive', 0, 1, 1)
SET IDENTITY_INSERT [gym].[MembershipStatus] OFF
ALTER TABLE [gym].[MembershipStatus] ADD  DEFAULT ('') FOR [Name]
GO
ALTER TABLE [gym].[MembershipStatus] ADD  DEFAULT ('') FOR [Description]
GO
ALTER TABLE [gym].[MembershipStatus] ADD  DEFAULT ((0)) FOR [AllowCheckin]
GO
ALTER TABLE [gym].[MembershipStatus] ADD  DEFAULT ((0)) FOR [IncludeInCollections]
GO
ALTER TABLE [gym].[MembershipStatus] ADD  DEFAULT ((0)) FOR [ScheduleFutureInvoices]
GO

我的代码的相关部分如下所示:

SqlCommand command = new SqlCommand(script, connection);
command.CommandType = CommandType.Text;
command.ExecuteNonQuery();

有任何想法吗?

4

11 回答 11

38

正如其他人提到的,按GO语句拆分您的字符串。"GO"但请注意,您的脚本的其他部分可能包含文本。您可能在 GO 语句之前或之后也有空格,并且您可能在 GO 语句之后的行上也有注释。其中任何一个在 SSMS 中都是有效的,因此您可能需要对其进行测试。

这是我使用的方法:

private static IEnumerable<string> SplitSqlStatements(string sqlScript)
{
    // Make line endings standard to match RegexOptions.Multiline
    sqlScript = Regex.Replace(sqlScript, @"(\r\n|\n\r|\n|\r)", "\n");

    // Split by "GO" statements
    var statements = Regex.Split(
            sqlScript,
            @"^[\t ]*GO[\t ]*\d*[\t ]*(?:--.*)?$",
            RegexOptions.Multiline |
            RegexOptions.IgnorePatternWhitespace |
            RegexOptions.IgnoreCase);

    // Remove empties, trim, and return
    return statements
        .Where(x => !string.IsNullOrWhiteSpace(x))
        .Select(x => x.Trim(' ', '\n'));
}
于 2013-09-03T16:21:32.723 回答
31

如果你想能够使用GO你将需要参考以下dll

Microsoft.SqlServer.ConnectionInfo.dll
Microsoft.SqlServer.Management.Sdk.Sfc.dll
Microsoft.SqlServer.Smo.dll Microsoft.SqlServer.SqlEnum.dll

然后像这样执行

 using (SqlConnection conn = new SqlConnection(connection))
 {
     Server db = new Server(new ServerConnection(conn));
     string script = File.ReadAllText(scriptPath);
     db.ConnectionContext.ExecuteNonQuery(script);      
 }
于 2013-09-03T16:19:06.653 回答
13

GO不是 SQL 的一部分,它是 SQL Server Management Studio 为您拆分脚本所做的事情。

您需要做的是将查询读入一个字符串,然后单独在GO一行上拆分(您可能需要为此使用正则表达式)

//Its better to dispose the SqlCommand, I also switched constructors so I could re-use the SqlCommand.
using(SqlCommand command = new SqlCommand())
{
    command.Connection = connection;

    var scripts = Regex.Split(script, @"^\w+GO$", RegexOptions.Multiline);
    foreach(var splitScript in scripts)
    {
        command.CommandText = splitScript;
        command.ExecuteNonQuery();
    }
}

查看马特约翰逊的答案,了解不那么幼稚的GO拆分实现。

于 2013-09-03T16:17:54.017 回答
9

GO 不是一个有效的 QA 命令,它是一个批处理分隔符……它由 Enterprise Manager 处理以分隔 SQL 脚本。因此,它将在企业管理器中工作,但不能在来自 C# 或其他外部程序的数据库调用中工作......

于 2013-09-03T16:12:13.020 回答
2

作为按摩脚本以使其可通过 C# 运行的替代方法,您可以使用该sqlcmd实用程序按原样运行它们。很多细节在:

http://technet.microsoft.com/en-us/library/ms180944.aspx

通过使用 sqlcmd,您可以编写任意数量的 SQL Server 生成脚本的执行脚本,而无需删除Go语句。

于 2013-09-04T04:15:51.860 回答
2

如另一个答案中所述,GO不支持。

您可以String.Split()在脚本上使用GO语句作为分隔符,并将每个段作为命令单独运行。

于 2013-09-03T16:16:09.220 回答
1
string[] commands = sql.Split( 
    new string[]{"GO\r\n", "GO ", "GO\t"}, StringSplitOptions.RemoveEmptyEntries );
foreach (string c in commands)
{
    command = new SqlCommand(c, masterConnection);
    command.ExecuteNonQuery();
}
}
catch (Exception e)
{
    MessageBox.Show(e.Message);
}
finally
{
    masterConnection.Close();
}
}

在这里找到。http://blogs.msdn.com/b/onoj/archive/2008/02/26/incorrect-syntax-near-go-sqlcommand-executenonquery.aspx

于 2014-09-09T19:14:04.453 回答
0

上面的答案有错误。我刚刚测试了一个可行的解决方案:您应该留出空间,';' 或 GO 前的新行

            var scripts = Regex.Split(statementText, @"(\s+|;|\n|\r)GO", RegexOptions.Multiline);
            foreach(var splitScript in scripts.Where(splitScript => !splitScript.IsNullOrWhiteSpace())) {
                cmd.CommandText = splitScript;
                cmd.ExecuteNonQuery();
            }
于 2015-02-11T21:15:23.127 回答
0

“iamkrillin”的答案还有 1 点,用于使用旧的 DLL 使其工作。

添加对这些 DLL 的引用后

Microsoft.SqlServer.ConnectionInfo.dll、Microsoft.SqlServer.Management.Sdk.Sfc.dll Microsoft.SqlServer.Smo.dll、Microsoft.SqlServer.SqlEnum.dll

从这样的地方:“C:\Program Files (x86)\Microsoft SQL Server\130\SDK\Assemblies\Microsoft.SqlServer.ConnectionInfo.dll”到项目中,我需要将以下“使用”指令添加到我的代码文件的顶部:

using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.Common;

....
string query = @" //sql multi-command text here"

using (SqlConnection thisconn = new SqlConnection(connectionString)) {
    Server db = new Server(new ServerConnection(thisconn));
    db.ConnectionContext.ExecuteNonQuery(query);
}
于 2019-01-08T19:12:17.497 回答
0

FluentMigrator 库中有非常好的SqlServerBatchParser类

支持GO GO 3陈述。

您需要安装FluentMigrator.Runner.SqlServer nuget 包

从 FluentMigrator 源代码中大量复制(稍作修改)的示例:

using FluentMigrator.Runner.BatchParser;
using FluentMigrator.Runner.BatchParser.SpecialTokenSearchers;
using FluentMigrator.Runner.BatchParser.Sources;

void Main()
{
    var connString = "Server=.;Database=mydb;Trusted_Connection=True;";
    var sql = @"select 1;
GO
SELECT 2;
GO 5";
    ExecuteBatchNonQuery(connString, sql);
}

public static void ExecuteBatchNonQuery(string ConnectionString, string sql)
{
    var sqlBatch = string.Empty;
    var conn = new SqlConnection(ConnectionString);
    conn.Open();

    try
    {
        var parser = new SqlServerBatchParser();
        parser.SqlText += (sender, args) => { sqlBatch = args.SqlText.Trim(); };
        parser.SpecialToken += (sender, args) =>
        {
            if (string.IsNullOrEmpty(sqlBatch))
                return;

            if (args.Opaque is GoSearcher.GoSearcherParameters goParams)
            {
                using (var command = conn.CreateCommand())
                {
                    command.CommandText = sqlBatch;

                    for (var i = 0; i != goParams.Count; ++i)
                    {
                        command.ExecuteNonQuery();
                    }
                }
            }

            sqlBatch = null;
        };

        using (var source = new TextReaderSource(new StringReader(sql), true))
        {
            parser.Process(source, stripComments: true);
        }

        if (!string.IsNullOrEmpty(sqlBatch))
        {
            using (var command = conn.CreateCommand())
            {
                command.CommandText = sqlBatch;
                command.ExecuteNonQuery();
            }
        }
    }
    catch (Exception ex)
    {
        using (var message = new StringWriter())
        {
            message.WriteLine("An error occured executing the following sql:");
            message.WriteLine(string.IsNullOrEmpty(sqlBatch) ? sql : sqlBatch);
            message.WriteLine("The error was {0}", ex.Message);

            throw new Exception(message.ToString(), ex);
        }
    }
    finally
    {
        conn?.Dispose();
    }
}

学分:https ://github.com/fluentmigrator/fluentmigrator/blob/v3.2.1/src/FluentMigrator.Runner.SqlServer/Processors/SqlServer/SqlServerProcessor.cs

于 2019-12-24T09:57:52.163 回答
0

只需将“GO”替换为“”即可。

SqlCommand command = new SqlCommand(script.Replace("GO", ""), connection);
command.CommandType = CommandType.Text;
command.ExecuteNonQuery();
于 2020-07-19T12:28:01.803 回答