web-dev-qa-db-fra.com

Instructions GO explosant l'exécution de SQL dans .NET

J'ai une très simple application C # de commande Shell qui exécute un script SQL généré par SQL Server pour la génération de scripts de schéma et de données. Il explose sur les déclarations "GO". Message d'erreur:

Syntaxe incorrecte près de 'GO'.

Voici le script complet 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

La section pertinente de mon code ressemble à ceci:

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

Des idées?

31
HerrimanCoder

Comme d'autres l'ont mentionné, divisez votre chaîne par des instructions GO. Mais faites attention, vous pouvez avoir le texte "GO" dans d'autres parties de votre script. Vous pouvez également avoir des espaces avant ou après l'instruction GO et des commentaires sur la ligne après l'instruction GO. Tout cela serait valable dans SSMS, vous pouvez donc le tester.

Voici la méthode que j'utilise:

private static IEnumerable<string> SplitSqlStatements(string sqlScript)
{
    // 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(' ', '\r', '\n'));
}
31
Matt Johnson

Si vous voulez pouvoir utiliser GO, vous devrez faire référence aux dll suivantes

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

Alors exécute comme si

 using (SqlConnection conn = new SqlConnection(connection))
 {
     Server db = new Server(new ServerConnection(conn));
     string script = File.ReadAllText(scriptPath);
     db.ConnectionContext.ExecuteNonQuery(script);      
 }
25
iamkrillin

GO ne fait pas partie de SQL, c'est quelque chose que SQL Server Management Studio fait pour vous permettre de scinder le script. 

Ce que vous devez faire est de lire la requête dans une chaîne puis de la scinder sur GO sur une ligne (vous pouvez utiliser Regex pour cela)

//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();
    }
}

Regardez La réponse de Matt Johnson pour une mise en œuvre moins naïve de la division GO.

10
Scott Chamberlain

GO n'est pas une commande QA valide, c'est un séparateur de lot ... Il est traité par Enterprise Manager pour séparer les scripts SQL. En tant que tel, il fonctionnera dans Enterprise Manager, mais pas dans les appels de base de données à partir de C # ou d'autres programmes externes ....

7
Sparky

Au lieu de masquer les scripts pour les rendre exécutables en C #, vous pouvez simplement les exécuter tels quels à l'aide de l'utilitaire sqlcmd. Beaucoup de détails à:

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

En utilisant sqlcmd, vous pouvez créer un script pour l'exécution de n'importe quel nombre de scripts générés par SQL Server, sans supprimer les instructions Go.

2
Eric King

Comme indiqué dans une autre réponse, GO n'est pas pris en charge.

Vous pouvez utiliser String.Split() sur votre script en utilisant vos instructions GO en tant que délimiteurs et exécuter chaque segment en tant que commande séparément.

1
Pablo Romeo
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();
}
}

Trouvé ici. http://blogs.msdn.com/b/onoj/archive/2008/02/26/incorrect-syntax-near-go-sqlcommand-executenonquery.aspx

1
user2011910

La réponse la plus en haut comporte une erreur ... Je viens de tester une solution qui fonctionne: Vous devriez laisser de la place, ";" ou nouvelle ligne avant 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();
            }
0
Sergey T

Un point supplémentaire à la réponse de "iamkrillin", pour utiliser les anciennes DLL pour le faire fonctionner.

après avoir ajouté les références à ces DLL 

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

à partir d'un emplacement comme celui-ci: "C:\Program Files (x86)\Microsoft SQL Server\130\SDK\Assemblies\Microsoft.SqlServer.ConnectionInfo.dll" au projet, j'avais besoin d'ajouter les éléments suivants "using" directives en haut de mon fichier de code:

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);
}
0
Andrew Raynier