如何使用C#执行.SQL脚本文件


140

我确定这个问题已经回答了,但是我无法使用搜索工具找到答案。

使用C#,我想运行一个.sql文件。sql文件包含多个sql语句,其中一些语句分成多行。我尝试读取文件,并尝试使用ODP.NET执行文件...但是,我认为ExecuteNonQuery并非真正旨在执行此操作。

因此,我尝试通过产生一个进程来使用sqlplus ...但是,除非我在UseShellExecute设置为true的情况下产生该进程,否则sqlplus会挂起并且永远不会退出。这是不起作用的代码。

Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "sqlplus";
p.StartInfo.Arguments = string.Format("xx/xx@{0} @{1}", in_database, s);
p.StartInfo.CreateNoWindow = true;

bool started = p.Start();
p.WaitForExit();

WaitForExit永远不会返回...。除非将UseShellExecute设置为true。UseShellExecute的副作用是您无法捕获重定向的输出。


8
里奇先生,您好,您的问题是关于Oracle的,并且您接受了针对sql server的解决方案?您将数据库更改为sql server?
Akshay J

Answers:


185
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.Common;
using System.IO;
using System.Data.SqlClient;

public partial class ExcuteScript : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
    string sqlConnectionString = @"Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=ccwebgrity;Data Source=SURAJIT\SQLEXPRESS";

    string script = File.ReadAllText(@"E:\Project Docs\MX462-PD\MX756_ModMappings1.sql");

    SqlConnection conn = new SqlConnection(sqlConnectionString);

    Server server = new Server(new ServerConnection(conn));

    server.ConnectionContext.ExecuteNonQuery(script);
    }
}

4
大!该解决方案为我工作,因为它能够删除和重新创建数据库以及添加表(通过引用的SQL脚本文件)。
食人魔赞美诗

11
从SQL Management Studio或osql命令运行脚本时,此方法不允许在脚本中使用“ GO”命令。 msdn.microsoft.com/en-us/library/ms188037.aspx
Rn222

20
Rn222:我认为您已经混淆了ExecuteNonQuery方法,SqlCommand.ExecuteNonQuery不允许使用“ GO”命令,但是Server.ConnectionContext.ExecuteNonQuery绝对可以(我现在正在使用它)。
PeterBelm 2012年

44
请注意,您需要将对项目的引用添加到Microsoft.SqlServer.ConnectionInfo,Microsoft.SqlServer.Management.Sdk和Microsoft.SqlServer.Smo,此答案才能起作用。
thomasb

8
对我来说,当使用.net 4.0 / 4.5以及引用110 \ SDK \ Assemblies时,它不起作用我发现的解决方案是将app.Config更改为<startup useLegacyV2RuntimeActivationPolicy="true"> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/> </startup>
Abir

107

我使用Microsoft.SqlServer.Management尝试了该解决方案,但它不适用于.NET 4.0,因此我仅使用.NET库框架编写了另一个解决方案。

string script = File.ReadAllText(@"E:\someSqlScript.sql");

// split script on GO command
IEnumerable<string> commandStrings = Regex.Split(script, @"^\s*GO\s*$", RegexOptions.Multiline | RegexOptions.IgnoreCase);

Connection.Open();
foreach (string commandString in commandStrings)
{
    if (!string.IsNullOrWhiteSpace(commandString.Trim()))
    {
        using(var command = new SqlCommand(commandString, Connection))
        {
            command.ExecuteNonQuery();
        }
    }
}     
Connection.Close();

究竟。使用完此解决方案后,它甚至都不会关闭文件。那可能很关键。
Mathias Lykkegaard Lorenzen'5

1
也可以使用“ RegexOptions.Multiline | RegexOptions.IgnoreCase”来匹配“ Go”或“ go”案例。
安库什

1
我认为也应该使用RegexOptions.CultureInvariant标志。
Dave Andersen

3
这不是100%有效:“ GO”可以接受数字参数。
nothrow 16/09/13

16

这适用于Framework 4.0或更高版本。支持“ GO”。还显示错误消息,行和sql命令。

using System.Data.SqlClient;

        private bool runSqlScriptFile(string pathStoreProceduresFile, string connectionString)
    {
        try
        {
            string script = File.ReadAllText(pathStoreProceduresFile);

            // split script on GO command
            System.Collections.Generic.IEnumerable<string> commandStrings = Regex.Split(script, @"^\s*GO\s*$",
                                     RegexOptions.Multiline | RegexOptions.IgnoreCase);
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                connection.Open();
                foreach (string commandString in commandStrings)
                {
                    if (commandString.Trim() != "")
                    {
                        using (var command = new SqlCommand(commandString, connection))
                        {
                        try
                        {
                            command.ExecuteNonQuery();
                        }
                        catch (SqlException ex)
                        {
                            string spError = commandString.Length > 100 ? commandString.Substring(0, 100) + " ...\n..." : commandString;
                            MessageBox.Show(string.Format("Please check the SqlServer script.\nFile: {0} \nLine: {1} \nError: {2} \nSQL Command: \n{3}", pathStoreProceduresFile, ex.LineNumber, ex.Message, spError), "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                            return false;
                        }
                    }
                    }
                }
                connection.Close();
            }
        return true;
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message, "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            return false;
        }
    }

3
好的代码,一件非常小的事情是,您将其包裹起来就不需要connection.Close()连接using
。– Amicable

做得好。它对我来说是“直截了当”。
Stephen85

8

将执行sql脚本的命令放入批处理文件中,然后运行以下代码

string batchFileName = @"c:\batosql.bat";
string sqlFileName = @"c:\MySqlScripts.sql";
Process proc = new Process();
proc.StartInfo.FileName = batchFileName;
proc.StartInfo.Arguments = sqlFileName;
proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
proc.StartInfo.ErrorDialog = false;
proc.StartInfo.WorkingDirectory = Path.GetDirectoryName(batchFileName);
proc.Start();
proc.WaitForExit();
if ( proc.ExitCode!= 0 )

在批处理文件中编写这样的内容(用于SQL Server的示例)

osql -E -i %1

6

这对我有用:

public void updatedatabase()
{

    SqlConnection conn = new SqlConnection("Data Source=" + txtserver.Text.Trim() + ";Initial Catalog=" + txtdatabase.Text.Trim() + ";User ID=" + txtuserid.Text.Trim() + ";Password=" + txtpwd.Text.Trim() + "");
    try
    {

        conn.Open();

        string script = File.ReadAllText(Server.MapPath("~/Script/DatingDemo.sql"));

        // split script on GO command
        IEnumerable<string> commandStrings = Regex.Split(script, @"^\s*GO\s*$", RegexOptions.Multiline | RegexOptions.IgnoreCase);
        foreach (string commandString in commandStrings)
        {
            if (commandString.Trim() != "")
            {
                new SqlCommand(commandString, conn).ExecuteNonQuery();
            }
        }
        lblmsg.Text = "Database updated successfully.";

    }
    catch (SqlException er)
    {
        lblmsg.Text = er.Message;
        lblmsg.ForeColor = Color.Red;
    }
    finally
    {
        conn.Close();
    }
}

4

增加了对surajits答案的其他改进:

using System;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.Common;
using System.IO;
using System.Data.SqlClient;

namespace MyNamespace
{
    public partial class RunSqlScript : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            var connectionString = @"your-connection-string";
            var pathToScriptFile = Server.MapPath("~/sql-scripts/") + "sql-script.sql";
            var sqlScript = File.ReadAllText(pathToScriptFile);

            using (var connection = new SqlConnection(connectionString))
            {
                var server = new Server(new ServerConnection(connection));
                server.ConnectionContext.ExecuteNonQuery(sqlScript);
            }
        }
    }
}

另外,我必须在项目中添加以下引用:

  • C:\Program Files\Microsoft SQL Server\120\SDK\Assemblies\Microsoft.SqlServer.ConnectionInfo.dll
  • C:\Program Files\Microsoft SQL Server\120\SDK\Assemblies\Microsoft.SqlServer.Smo.dll

我不知道这些是不是正确的dll:s,因为C:\ Program Files \ Microsoft SQL Server中有多个文件夹,但是在我的应用程序中这两个起作用。


这在.Net 4.7中对我有用。我不需要surajit提到的其他dll。但是,我必须为Microsoft.SqlServer.ConnectionInfo和Microsoft.SqlServer.Smo使用版本13.0.0.0,因为实例化ServerConnection时13.100.0.0引发了异常。
凯文·菲希特

4

我通过阅读手册设法找到了答案:)

此摘录来自MSDN

该代码示例通过在p.WaitForExit之前调用p.StandardOutput.ReadToEnd来避免死锁情况。如果父进程在p.StandardOutput.ReadToEnd之前调用p.WaitForExit,并且子进程写入足够的文本以填充重定向的流,则可能导致死锁。父进程将无限期地等待子进程退出。子进程将无限期等待父进程从完整的StandardOutput流中读取。

从标准输出和标准错误流中读取所有文本时,也会出现类似的问题。例如,以下C#代码对两个流都执行读取操作。

将代码转换为此;

Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "sqlplus";
p.StartInfo.Arguments = string.Format("xxx/xxx@{0} @{1}", in_database, s);

bool started = p.Start();
// important ... read stream input before waiting for exit.
// this avoids deadlock.
string output = p.StandardOutput.ReadToEnd();

p.WaitForExit();

Console.WriteLine(output);

if (p.ExitCode != 0)
{
    Console.WriteLine( string.Format("*** Failed : {0} - {1}",s,p.ExitCode));
    break;
}

现在可以正确退出了。


2
有关sqlplus的提示:如果您想知道脚本执行是否成功,可以在脚本的开头添加WHENEVER SQLERROR EXIT SQL.SQLCODE。这样,sqlplus进程将sql错误号作为返回代码返回。
devdimi

有完整的完整源代码示例吗?什么是in_database,s?
Kiquenet 2011年

2
这对我不起作用。p.StandardOutput.ReadToEnd();永不退出
Louis Rhys'2

2

有两点需要考虑。

1)此源代码对我有用:

private static string Execute(string credentials, string scriptDir, string scriptFilename)
{ 
  Process process = new Process();
  process.StartInfo.UseShellExecute = false;
  process.StartInfo.WorkingDirectory = scriptDir;
  process.StartInfo.RedirectStandardOutput = true;
  process.StartInfo.FileName = "sqlplus";
  process.StartInfo.Arguments = string.Format("{0} @{1}", credentials, scriptFilename);
  process.StartInfo.CreateNoWindow = true;

  process.Start();
  string output = process.StandardOutput.ReadToEnd();
  process.WaitForExit();

  return output;
}

我将工作目录设置为脚本目录,以便脚本中的子脚本也可以工作。

称它为 Execute("usr/pwd@service", "c:\myscripts", "script.sql")

2)您必须使用以下语句结束SQL脚本 EXIT;


1

使用EntityFramework,您可以使用这样的解决方案。我使用此代码初始化e2e测试。为了防止sql注入攻击,请确保不要基于用户输入生成此脚本或为此使用命令参数(请参见接受参数的ExecuteSqlCommand重载)。

public static void ExecuteSqlScript(string sqlScript)
{
    using (MyEntities dataModel = new MyEntities())
    {
        // split script on GO commands
        IEnumerable<string> commands = 
            Regex.Split(
                sqlScript, 
                @"^\s*GO\s*$",
                RegexOptions.Multiline | RegexOptions.IgnoreCase);

        foreach (string command in commands)
        {
            if (command.Trim() != string.Empty)
            {
                dataModel.Database.ExecuteSqlCommand(command);
            }
        }              
    }
}

-1

我找不到任何准确有效的方法来执行此操作。因此,整整一天之后,我带来了从不同来源获得的混合代码,并试图完成工作。

但是,ExecuteNonQuery: CommandText property has not been Initialized即使它成功运行了脚本文件,它仍然会生成异常-在我的情况下,它成功创建了数据库并在第一次启动时插入了数据。

public partial class Form1 : MetroForm
{
    SqlConnection cn;
    SqlCommand cm;
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        if (!CheckDatabaseExist())
        {
            GenerateDatabase();
        }
    }

    private bool CheckDatabaseExist()
    {
        SqlConnection con = new SqlConnection(@"Data Source=.\SQLEXPRESS;Initial Catalog=SalmanTradersDB;Integrated Security=true");
        try
        {
            con.Open();
            return true;
        }
        catch
        {
            return false;
        }
    }

    private void GenerateDatabase()
    {

        try
        {
            cn = new SqlConnection(@"Data Source=.\SQLEXPRESS;Initial Catalog=master;Integrated Security=True");
            StringBuilder sb = new StringBuilder();
            sb.Append(string.Format("drop databse {0}", "SalmanTradersDB"));
            cm = new SqlCommand(sb.ToString() , cn);
            cn.Open();
            cm.ExecuteNonQuery();
            cn.Close();
        }
        catch
        {

        }
        try
        {
            //Application.StartupPath is the location where the application is Installed
            //Here File Path Can Be Provided Via OpenFileDialog
            if (File.Exists(Application.StartupPath + "\\script.sql"))
            {
                string script = null;
                script = File.ReadAllText(Application.StartupPath + "\\script.sql");
                string[] ScriptSplitter = script.Split(new string[] { "GO" }, StringSplitOptions.None);
                using (cn = new SqlConnection(@"Data Source=.\SQLEXPRESS;Initial Catalog=master;Integrated Security=True"))
                {
                    cn.Open();
                    foreach (string str in ScriptSplitter)
                    {
                        using (cm = cn.CreateCommand())
                        {
                            cm.CommandText = str;
                            cm.ExecuteNonQuery();
                        }
                    }
                }
            }
        }
        catch
        {

        }

    }

}

我找不到任何确切有效的方法来执行此操作。因此,整整一天之后,我带来了从不同来源获得的混合代码,并试图完成工作。所以我将它们全部合并并得出结果。但是它仍在生成异常“ ExecuteNonQuery:CommandText属性尚未初始化”。尽管它成功运行了脚本文件(对于我来说,是成功创建数据库并在第一次启动时插入了数据)。
穆罕默德·萨尔曼
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.