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サーバー向けのソリューションを受け入れましたか?DBをSQLサーバーに変更しましたか?
Akshay J 2012

回答:


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スクリプトファイルを介して)テーブルを追加できるという点で私にとってはうまくいきました。
Ogre Psalm33 2009

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 2012

1
「RegexOptions.Multiline | RegexOptions.IgnoreCase」を使用して、「Go」または「go」のケースにも一致させます。
Ankush、2012年

1
RegexOptions.CultureInvariantフラグも使用する必要があると思います。
デイブアンデルセン

3
これは100%機能していません。'GO 'は数値パラメータを受け入れる場合があります。
nothrow

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
ニースのコード、1つの非常にマイナーな事はそれは必要としないでconnection.Close()、接続が終了しますusing。あなたはそれを包みました
友好的

すごい仕事。それは私にとって「箱から出してすぐ」機能しました。
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サーバーのサンプル)

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

スラジットの答えに追加の改善を追加しました:

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

C:\ Program Files \ Microsoft SQL Serverにいくつかのフォルダーがあるため、これらが適切なdll:sであるかどうかはわかりませんが、私のアプリケーションではこれら2つが機能します。


これは私にとって.Net 4.7で機能しました。私は、surajitによって言及された他のdllを必要としませんでした。ただし、13.100.0.0がServerConnectionのインスタンス化時に例外をスローしたため、Microsoft.SqlServer.ConnectionInfoとMicrosoft.SqlServer.Smoの両方にバージョン13.0.0.0を使用する必要がありました。
Kevin Fichter

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 2009年

完全な完全なソースコードサンプルはありますか?in_databaseとは何ですか?
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
        {

        }

    }

}

これを行うための正確で有効な方法を見つけることができませんでした。そのため、1日が経過した後、さまざまなソースから達成されたこの混合コードを使用して、仕事を成し遂げようとしました。だから私はそれらすべてをマージして結果を作りました。しかし、それでも例外「ExecuteNonQuery:CommandTextプロパティが初期化されていません」を生成しています。スクリプトファイルは正常に実行されますが(私の場合、データベースを正常に作成し、最初の起動時にデータを挿入します)。
ムハンマドサルマン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.