Как выполнить файл сценария .SQL с помощью C #

101

Я уверен, что на этот вопрос уже был дан ответ, однако я не смог найти ответ, используя инструмент поиска.

С помощью С# я хочу запустить файл .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 является то, что вы не можете захватить перенаправленный вывод.

  • 5
    Здравствуйте, мистер Рич, ваш вопрос касался Oracle, и вы приняли решение, которое было для сервера sql? Вы изменили свою БД на SQL Server?
Теги:
scripting

8 ответов

138
Лучший ответ
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
    Этот метод не позволяет использовать команду «GO» в вашем сценарии, которая разрешена при запуске сценария из SQL Management Studio или команды osql. msdn.microsoft.com/en-us/library/ms188037.aspx
Показать ещё 12 комментариев
84

Я пробовал это решение с помощью 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 (commandString.Trim() != "")
    {
       using(var command = new SqlCommand(commandString, Connection))
       {
          command.ExecuteNonQuery();
       }
    }
  }     
  Connection.Close();
  • 0
    Именно так. Это решение даже не закроет файл после его использования. Это может быть критично.
  • 3
    File.ReadAllText закроет файл после прочтения.
Показать ещё 3 комментария
9

Поместите команду для выполнения sql script в пакетный файл, затем выполните приведенный ниже код

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
4

Мне удалось найти ответ, прочитав руководство:)

Эта выдержка из MSDN

Пример кода исключает тупик условие по телефону p.StandardOutput.ReadToEnd перед p.WaitForExit. Условие взаимоблокировки может возникнуть, если вызов родительского процесса p.WaitForExit перед p.StandardOutput.ReadToEnd и дочерний процесс записывает достаточно текста в заполните перенаправленный поток. Родитель процесс будет ждать бесконечно дочерний процесс для выхода. Ребенок процесс будет ждать бесконечно родитель должен читать полностью Поток StandardOutput.

Аналогичная проблема возникает при чтении весь текст из стандартного вывода и стандартные потоки ошибок. Для Например, следующий код С# выполняет операцию чтения на обоих потоков.

Вводит код в это:

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 в качестве кода возврата.
  • 0
    какой-нибудь полный пример исходного кода? что такое in_database, s ??
Показать ещё 1 комментарий
3

Это работает на платформе 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;
        }
    }
  • 1
    Хороший код, одна очень незначительная вещь заключается в том, что connection.Close() не нужно. connection.Close() соединение будет закрыто с using вы его завернули.
3

Добавлены дополнительные улучшения для ответа 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 есть несколько папок, но в моем приложении эти две работы.

  • 0
    Это сработало для меня в .Net 4.7. Я не нуждался в других dll, упомянутых surajit. Однако мне пришлось использовать версию 13.0.0.0 как для Microsoft.SqlServer.ConnectionInfo, так и для Microsoft.SqlServer.Smo, так как 13.100.0.0 вызывал исключения при создании экземпляра ServerConnection.
2

Это работает для меня:

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

Я устанавливаю рабочий каталог в каталог script, так что также работают субскрипты в script.

Назовите его, например. как Execute("usr/pwd@service", "c:\myscripts", "script.sql")

2) Вы должны завершить свой SQL script с помощью инструкции EXIT;

Ещё вопросы

Сообщество Overcoder
Наверх
Меню