Làm cách nào để thực thi tệp tập lệnh .Query bằng c #


140

Tôi chắc chắn rằng câu hỏi này đã được trả lời, tuy nhiên tôi không thể tìm thấy câu trả lời bằng công cụ tìm kiếm.

Sử dụng c # Tôi muốn chạy tệp .sql. Tệp sql chứa nhiều câu lệnh sql, một số trong đó được chia nhỏ trên nhiều dòng. Tôi đã thử đọc tệp và thử thực thi tệp bằng ODP.NET ... tuy nhiên tôi không nghĩ ExecuteNonQuery thực sự được thiết kế để làm việc này.

Vì vậy, tôi đã thử sử dụng sqlplus thông qua việc sinh ra một quy trình ... tuy nhiên trừ khi tôi sinh ra quy trình với UseShellExecute được đặt thành sqlplus thực sẽ bị treo và không bao giờ thoát. Đây là mã KHÔNG LÀM VIỆC.

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 không bao giờ trả lại .... Trừ khi tôi đặt UseShellExecute thành true. Một tác dụng phụ của UseShellExecute là bạn không thể nắm bắt được đầu ra được chuyển hướng.


8
Xin chào ông Rich, câu hỏi của bạn là về Oracle và bạn đã chấp nhận một giải pháp dành cho máy chủ sql? Bạn đã thay đổi DB của bạn thành máy chủ sql?
Akshay

Câu trả lời:


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
Tuyệt quá! Giải pháp này hoạt động với tôi vì có thể thả và tạo lại cơ sở dữ liệu và thêm các bảng (thông qua tệp tập lệnh SQL được tham chiếu).
Ogre Psalm33

11
Phương pháp này không cho phép sử dụng lệnh "GO" trong tập lệnh của bạn, điều này được cho phép khi bạn chạy tập lệnh từ SQL Management Studio hoặc lệnh osql. msdn.microsoft.com/en-us/l
Library / ms188037.aspx

20
Rn222: Tôi nghĩ rằng bạn đã nhầm lẫn các phương thức ExecuteNonQuery, SqlCommand.ExecuteNonQuery sẽ không cho phép sử dụng các lệnh "GO", tuy nhiên Server.ConnectionContext.ExecuteNonQuery chắc chắn (tôi đang sử dụng nó ngay bây giờ).
PeterBelm

44
Lưu ý rằng bạn cần thêm các tham chiếu đến dự án, vào Microsoft.SqlServer.ConnectionInfo, Microsoft.SqlServer.Man Quản lý.Sdk và Microsoft.SqlServer.Smo để câu trả lời này hoạt động.
thomasb

8
Đối với tôi nó không hoạt động khi sử dụng .net 4.0 / 4.5, khi tham khảo 110 \ SDK \ Assemblies Giải pháp tôi tìm thấy là thay đổi ứng dụng.Config thành<startup useLegacyV2RuntimeActivationPolicy="true"> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/> </startup>
Abir

107

Tôi đã thử giải pháp này với Microsoft.SqlServer. Quản lý nhưng nó không hoạt động tốt với .NET 4.0 vì vậy tôi đã viết một giải pháp khác chỉ sử dụng .NET libs framework.

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

Chính xác. Giải pháp này thậm chí sẽ không đóng tệp sau khi sử dụng xong. Điều đó có thể rất quan trọng.
Mathias Lykkegaard Lorenzen

1
Sử dụng "RegexOptions.Multiline | RegexOptions.IgnoreCase" để khớp với các trường hợp "Đi" hoặc "đi".
Ankush

1
Tôi nghĩ rằng cờ RegexOptions.CultInvariant cũng nên được sử dụng.
Dave Andersen

3
Điều này không hoạt động 100%: 'GO' có thể chấp nhận tham số số.
nothrow

16

Điều này hoạt động trên khung 4.0 hoặc cao hơn. Hỗ trợ "GO". Cũng hiển thị thông báo lỗi, dòng và lệnh 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
Mã đẹp, một điều rất nhỏ là không cần connection.Close()kết nối sẽ bị đóng bởi usingbạn đã bọc nó.
Amitable

Công việc tuyệt vời Nó hoạt động "thẳng ra khỏi hộp" đối với tôi.
Stephen85

8

Đặt lệnh để thực thi tập lệnh sql vào một tệp bó sau đó chạy đoạn mã dưới đây

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 )

trong tệp bó ghi một cái gì đó như thế này (mẫu cho máy chủ sql)

osql -E -i %1

6

Điều này làm việc cho tôi:

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

Đã thêm các cải tiến bổ sung cho câu trả lời 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);
            }
        }
    }
}

Ngoài ra, tôi đã phải thêm các tài liệu tham khảo sau vào dự án của mình:

  • 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

Tôi không biết đó có phải là những dll: s phù hợp để sử dụng hay không vì có một số thư mục trong C: \ Program Files \ Microsoft SQL Server nhưng trong ứng dụng của tôi, hai ứng dụng này hoạt động.


Điều này làm việc cho tôi trong .Net 4.7. Tôi không cần các dll khác được đề cập bởi surajit. Tuy nhiên, tôi đã phải sử dụng phiên bản 13.0.0.0 cho cả Microsoft.SqlServer.ConnectionInfo và Microsoft.SqlServer.Smo, vì 13.100.0.0 đã ném ngoại lệ khi khởi tạo ServerConnection.
Kevin Fichter

4

Tôi quản lý để tìm ra câu trả lời bằng cách đọc hướng dẫn :)

Trích xuất này từ MSDN

Ví dụ mã tránh điều kiện bế tắc bằng cách gọi p.St ChuẩnOutput.ReadToEnd trước p.WaitForExit. Một điều kiện bế tắc có thể xảy ra nếu tiến trình cha gọi p.WaitForExit trước p.St ChuẩnOutput.ReadToEnd và tiến trình con ghi đủ văn bản để điền vào luồng được chuyển hướng. Quá trình cha sẽ chờ vô thời hạn để tiến trình con thoát ra. Quá trình con sẽ chờ vô thời hạn để cha mẹ đọc từ luồng StandardOutput đầy đủ.

Có một vấn đề tương tự khi bạn đọc tất cả văn bản từ cả hai luồng lỗi tiêu chuẩn và đầu ra tiêu chuẩn. Ví dụ: mã C # sau đây thực hiện thao tác đọc trên cả hai luồng.

Biến mã thành này;

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;
}

Mà bây giờ thoát chính xác.


2
Một mẹo liên quan đến sqlplus: nếu bạn muốn biết liệu thực thi tập lệnh có thành công hay không, bạn có thể thêm WHENEVER SQLERROR EXIT SQL.SQLCODE ở đầu tập lệnh. Bằng cách này, quá trình sqlplus trả về số lỗi sql dưới dạng mã trả về.
devdimi

bất kỳ mẫu mã nguồn đầy đủ đầy đủ? In_database là gì, s ??
Kiquenet

2
điều này không làm việc cho tôi. p.StandardOutput.ReadToEnd();không bao giờ thoát
Louis Rhys

2

Có hai điểm cần quan tâm.

1) Mã nguồn này làm việc cho tôi:

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;
}

Tôi đặt thư mục làm việc vào thư mục script, để các tập lệnh con trong tập lệnh cũng hoạt động.

Gọi nó như là Execute("usr/pwd@service", "c:\myscripts", "script.sql")

2) Bạn phải hoàn thiện tập lệnh SQL của mình bằng câu lệnh EXIT;


1

Sử dụng EntityFramework, bạn có thể đi với một giải pháp như thế này. Tôi sử dụng mã này để khởi tạo các bài kiểm tra e2e. Để ngăn chặn các cuộc tấn công tiêm sql, hãy đảm bảo không tạo tập lệnh này dựa trên đầu vào của người dùng hoặc sử dụng các tham số lệnh cho việc này (xem quá tải của ExecuteSqlCommand chấp nhận tham số).

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

Tôi không thể tìm thấy bất kỳ cách chính xác và hợp lệ để làm điều này. Vì vậy, sau cả một ngày, tôi đã đến với mã hỗn hợp này đạt được từ các nguồn khác nhau và cố gắng hoàn thành công việc.

Nhưng nó vẫn tạo ra một ngoại lệ ExecuteNonQuery: CommandText property has not been Initializedmặc dù nó chạy thành công tệp script - trong trường hợp của tôi, nó tạo thành công cơ sở dữ liệu và chèn dữ liệu vào lần khởi động đầu tiên.

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
        {

        }

    }

}

Tôi không thể tìm thấy bất kỳ cách chính xác và hợp lệ để làm điều này. Vì vậy, sau cả một ngày, tôi đã đến với mã hỗn hợp này đạt được từ các nguồn khác nhau và cố gắng hoàn thành công việc. Vì vậy, tôi đã hợp nhất tất cả và đưa ra kết quả. Nhưng nó vẫn đang tạo ra một ngoại lệ "ExecuteNonQuery: Thuộc tính CommandText chưa được khởi tạo." Mặc dù nó chạy thành công tệp tập lệnh (Trong trường hợp của tôi, tạo thành công cơ sở dữ liệu và chèn dữ liệu vào lần khởi động đầu tiên).
Muhammad Salman
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.