Возможно ли выполнить многие хранимые процедуры за одну операцию?

Я читаю xml файлы для обновления базы данных. Я получаю около 500 файлов xml, и я хочу обрабатывать их как можно быстрее.

Все операции с базой данных выполняются с использованием хранимых процедур.

В каждом XML файле содержится около 35 различных хранимых процедур.

Первоначально я написал код, подобный этому

var cmd = new SqlCommand("EXEC UpdateTeamStats("+teamId+","+points+")");
cmd.CommandType = CommandType.Text;

но, пройдя несколько лучших практик, я изменил его на

var cmd = new SqlCommand("UpdateTeamStats");
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("teamId", 21);
cmd.Parameters.Add("points", 2);

из-за большого количества хранимых процедур, вызываемых из программы, я понял, что для оптимизации нужно сделать меньшее количество вызовов.

Итак, я хочу собрать все 35 хранимых процедур и выполнить их за один раз.

Хранимые процедуры различаются с разными параметрами, и я не знаю способ собрать и выполнить их вместе после изменения параметров, которые я сделал выше.

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

Возможно ли это сделать полностью на С#?

Или есть какой-то другой лучший метод для очереди на сохраненныепроцедуры и быстрого запуска их

Ответ 1

Загрузите блок данных Microsoft Applications из

http://www.microsoft.com/download/en/details.aspx?id=435

Хорошо, но как его использовать?

Использование этого класса-оболочки довольно просто.

DAC DC = new DAC();
DC.StoredProcedure = "nProc_InsertOrder";
DC.Params.Add("@OrderId", SqlDbType.VarChar, "Order1" );
DC.Params.Add("@CustomerName", SqlDbType.VarChar, "test");
DAC.Commands.Add(DC);

DC = new DAC();
DC.StoredProcedure = "nProc_InsertOrderLineItems";
DC.Params.Add("@OrderId", SqlDbType.VarChar, "Order1" );
DC.Params.Add("@OrderLineId", SqlDbType.VarChar, "A1");
DAC.Commands.Add(DC);

DC = new DAC();
DC.StoredProcedure = "nProc_InsertOrderLineItems";
DC.Params.Add("@OrderId", SqlDbType.VarChar, "Order1" );
DC.Params.Add("@OrderLineId", SqlDbType.VarChar, "A2");
DAC.Commands.Add(DC);

DC = new DAC();
DC.StoredProcedure = "nProc_CreateBill";
DC.Params.Add("@BillDate", SqlDbType.DateTime, DateTime.Now);
DC.Params.Add("@BillId", SqlDbType.VarChar, "Bill1");
DAC.Commands.Add(DC);
DAC.ExecuteBatch();

Если вставка заказа не удалась, счет не должен создаваться. Аналогичным образом, если строки не удались, то порядок не должен создаваться. Мы достигаем этого лишь несколькими строками кода через ADO.Net.

В этом примере, пока мы не вызываем ExecuteBatch, мы фактически не вставляем записи, а готовим объект для создания пакетных обновлений.

Ответ 2

Лучшим решением imo было бы написать одну хранимую процедуру с переданным в таблице параметром, содержащим список всех параметров для xml файла. Затем в этом сохраненном proc вызовет все остальные сохраненные procs для каждой записи в параметре table-value.

Если это не так, вы можете использовать SqlCommand типа text вместо хранимой процедуры и просто построить команду по мере ее запуска и выполнить. Вы можете использовать параметры так же, как сейчас, или просто написать динамический sql.

Ответ 3

Вы можете создать CommandQueue через шаблон команды и создать делегат в команде, которая соответствует всем вашим требованиям. вызов хранимой процедуры; по внешности, что-то вроде:

public class CommandQueue
{
    private Connection _connexion = new Connection(); // Set this up somehow.

    // Other methods to handle the concurrency/ calling/ transaction etc.

    public Func<string, Dictionary<string, int>, bool> CallStoredProcedure = (procedureName, parameterValueMap) =>
    {
      cmd.Connection = GetConnexion();
      var cmd = new SqlCommand(procedureName);
      cmd.CommandType = CommandType.StoredProcedure;

      foreach (var parameterValueMapping in parameterValueMap)
      {
        cmd.Parameters.Add(parameterValueMapping.Key, parameterValueMapping.Value);
      }

      var success = cmd.ExecuteNonQuery();

      return success;
    }

    private Connection GetConnexion()
    {
      return _connexion;
    }
}

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

Собственно, глядя на класс SQLCommand, вы можете сделать асинхронные вызовы. Таким образом, вы должны асинхронно вызывать каждую из ваших хранимых процедур, настраивать делегат, когда каждый завершает и переносит все это в транзакцию, чтобы вы могли откатить их обратно, когда вам нужно, вызвав Cancel() для каждой команды. Я бы, вероятно, все еще использовал CommandQueue, чтобы абстрагировать это, так как я бы предположил, что вы, вероятно, измените его позже!

Я думаю, что я все еще буду инкапсулировать вызов хранимой процедуры с делегатом на CommandQueue, чтобы в этом абстрагироваться от деталей хранимой процедуры и упростить процесс для кого-то другого, а также проще поддерживать, Это будет намного проще, если вы добавите новые хранимые процедуры или измените имя или что-то еще. Вы можете установить статический список, который может содержать все делегаты, или статический список с необходимыми хранимыми процедурами, и использовать делегат для передачи только параметров.

Ответ 4

Лично, и, по моему опыту с использованием ADO.NET, я думаю, что у вас не будет никаких проблем, выполняющих их как отдельные с помощью single SqlConnection.

Это имеет преимущество использования .NET amazing пула соединений, позволяет вам работать/настраивать/взаимодействовать с каждой командой индивидуально и ездить на (сокращение количества брокерских услуг на тривиальное количество).

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

Например:

using (var conn = new SqlConnection("connection string"))
{
    using (var cmd = new SqlCommand())
    {
        cmd.Connection = conn;
        cmd.CommandType = CommandType.StoredProcedure;

        //ready to query 
        conn.Open();

        cmd.CommandText = "UpdateTeamStats";
        var teamIdParam = new SqlParameter("teamId", 21);
        var pointsParam = new SqlParameter("points", 2);

        cmd.Parameters.Add(teamIdParam);
        cmd.Parameters.Add(pointsParam);

        cmd.ExecuteNonQuery(); //OR if you're async cmd.ExecuteNonQueryAsync();

        //the rest of your executions

        conn.Close();
    }
}

И если я немного помолчу, вы можете использовать библиотеку, такую ​​как my DbConnect, что уменьшает в:

using (var db = new DbConnect("connection string"))
{
    db.SetSqlCommand("UpdateTeamStats");
    db.AddParameter("teamId", 21);
    db.AddParameter("points", 2);

    db.ExecuteNonQuery().Wait(); //OR if youre async await db.ExecuteNonQuery();

    db.ClearParameters();
    db.SetSqlCommand("some other proc");

    //rest of executions
}