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
안녕하세요 Mr. Rich, 귀하의 질문은 Oracle에 관한 것이며 귀하는 SQL Server 용 솔루션을 수락 했습니까? DB를 SQL Server로 변경 했습니까?
Akshay J

답변:


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 스크립트 파일을 통해 테이블을 추가 할 수있게되었습니다.
오우거 Psalm33

11
이 방법을 사용하면 SQL Management Studio 또는 osql 명령에서 스크립트를 실행할 때 허용되는 스크립트에서 "GO"명령을 사용할 수 없습니다. msdn.microsoft.com/ko-kr/library/ms188037.aspx
Rn222

20
Rn222 : ExecuteNonQuery 메서드를 혼동했다고 생각합니다. SqlCommand.ExecuteNonQuery에서는 "GO"명령을 사용할 수 없지만 Server.ConnectionContext.ExecuteNonQuery는 확실히 사용합니다 (지금 사용하고 있습니다).
PeterBelm

44
이 답변이 작동하려면 프로젝트에 대한 참조를 Microsoft.SqlServer.ConnectionInfo, Microsoft.SqlServer.Management.Sdk 및 Microsoft.SqlServer.Smo에 추가해야합니다.
thomasb

8
참조 할 때, 4.0 / 4.5 NET을 사용하는 경우 나에게 그것은 작동하지 않았다 110 \ SDK \ 어셈블리 나는의 app.config에 변경 된 발견 된 솔루션<startup useLegacyV2RuntimeActivationPolicy="true"> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/> </startup>
아비르

107

이 솔루션을 Microsoft.SqlServer.Management로 시도했지만 .NET 4.0에서는 제대로 작동하지 않으므로 .NET libs 프레임 워크만을 사용하여 다른 솔루션을 작성했습니다.

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

1
"Go"또는 "go"케이스와도 일치하도록 "RegexOptions.Multiline | RegexOptions.IgnoreCase"를 사용하십시오.
Ankush

1
RegexOptions.CultureInvariant 플래그도 사용해야한다고 생각합니다.
Dave Andersen

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
좋은 코드, 아주 사소한 것 중 하나 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 서버의 샘플)

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

C : \ Program Files \ Microsoft SQL Server에 여러 폴더가 있기 때문에 올바른 dll : s인지는 모르겠지만 내 응용 프로그램에서는이 두 가지가 작동합니다.


이것은 .Net 4.7에서 나를 위해 일했습니다. surajit이 언급 한 다른 dll이 필요하지 않았습니다. 그러나 ServerConnection을 인스턴스화 할 때 13.100.0.0에서 예외가 발생했기 때문에 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

완전한 완전한 소스 코드 샘플? in_database 란 무엇입니까?
Kiquenet

2
이것은 나를 위해 작동하지 않습니다. p.StandardOutput.ReadToEnd();절대로 나가지
Louis Rhys

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 속성이 초기화되지 않았습니다."라는 예외가 발생합니다. 스크립트 파일을 성공적으로 실행하지만 (제 경우에는 데이터베이스를 만들고 처음 시작할 때 데이터를 삽입하십시오).
Muhammad Salman
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.