Intereting Posts
Передача данных между C ++ (MFC) и C # Это ошибка в динамике? Создайте экземпляр производного classа из базового classа Создает ли String.Replace () новую строку, если заменить ее нет? Как скопировать файл, который находится в том же каталоге, что и установщик EF Core 2.0.0 Query Filter – это кеширование TenantId (обновлено для 2.0.1+) RestSharp – Как обрабатывать ответы, отличные от 200? RestClient выбрасывает исключение в Execute Как отключить определенный пункт меню из меню в коде asp.net C # InvalidOperationException: операция Undo столкнулась с контекстом, который отличается от того, что было применено в соответствующей операции Set Чтение данных JSON в ASP.Net Core MVC Как определить текущее имя и версию сфокусированного процесса в C # Как загрузить файл изображения в профиль пользователя Active Directory на C #? Как использовать SQLInstallDriverEx в C #? Вложенные макеты для MVC5 Как добавить разделитель в меню WinForms на C #?

Несколько параллельных вставок C # в базе данных

Я имею datatable с вокруг 3000 рядов. Каждая из этих строк должна быть вставлена ​​в таблицу базы данных. В настоящее время я запускаю цикл foreach:

obj_AseCommand.CommandText = sql_proc; obj_AseCommand.CommandType = CommandType.StoredProcedure; obj_AseCommand.Connection = db_Conn; obj_AseCommand.Connection.Open(); foreach (DataRow dr in dt.Rows) { obj_AseCommand.Parameters.AddWithValue("@a", dr["a"]); obj_AseCommand.Parameters.AddWithValue("@b", dr["b"]); obj_AseCommand.Parameters.AddWithValue("@c", dr["c"]); obj_AseCommand.ExecuteNonQuery(); obj_AseCommand.Parameters.Clear(); } obj_AseCommand.Connection.Close(); 

Не могли бы вы посоветовать, как я могу выполнить параллельное выполнение базы данных SP в базе данных, так как вышеупомянутый подход занимает около 10 минут, чтобы вставить 3000 строк.

редактировать

Оглядываясь назад, использование Parallel.ForEach для параллелизации вложений БД немного расточительно, так как оно также будет потреблять stream для каждого соединения. Возможно, еще лучшим параллельным решением будет использование асинхронных версий операций System.Data Db, таких как ExecuteNonQueryAsync , запуск выполнения (одновременно), а затем использование функции await Task.WhenAll() для ожидания после завершения – это позволит избежать Thread накладной для вызывающего абонента, хотя общая производительность Db, скорее всего, не будет более быстрой. Подробнее здесь

Исходный ответ, несколько параллельных вставок в базу данных

Вы можете сделать это параллельно, используя TPL, например, специально с перегрузкой localInit Parallel.ForEach . Вы почти наверняка захотите взглянуть на регулирование количества параллелизма, настроив MaxDegreeOfParalelism, чтобы вы не наводнили вашу базу данных:

 Parallel.ForEach(dt.Rows, // Adjust this for optimum throughput vs minimal impact to your other DB users new ParallelOptions { MaxDegreeOfParallelism = 4 }, () => { var con = new SqlConnection(); var cmd = con.CreateCommand(); cmd.CommandText = sql_proc; cmd.CommandType = CommandType.StoredProcedure; con.Open(); cmd.Parameters.Add(new SqlParameter("@a", SqlDbType.Int)); // NB : Size sensitive parameters must have size cmd.Parameters.Add(new SqlParameter("@b", SqlDbType.VarChar, 100)); cmd.Parameters.Add(new SqlParameter("@c", SqlDbType.Bit)); // Prepare won't help with SPROCs but can improve plan caching for adhoc sql // cmd.Prepare(); return new {Conn = con, Cmd = cmd}; }, (dr, pls, localInit) => { localInit.Cmd.Parameters["@a"] = dr["a"]; localInit.Cmd.Parameters["@b"] = dr["b"]; localInit.Cmd.Parameters["@c"] = dr["c"]; localInit.Cmd.ExecuteNonQuery(); return localInit; }, (localInit) => { localInit.Cmd.Dispose(); localInit.Conn.Dispose(); }); 

Заметки:

  • Если вы действительно не знаете, что делаете, в целом мы должны оставить TPL, чтобы определить степень параллелизма. Тем не менее, в зависимости от того, сколько конфликтов (чтение: блокировки для работы с базой данных) для ресурсов, может потребоваться ограничение верхнего предела одновременных задач (пробная версия и ошибка могут быть полезны, например, попробовать с совпадениями 4, 8, 16 одновременных задач и т. Д. см., что дает большую пропускную способность, и контролирует блокировку и загрузку ЦП на вашем сервере Sql.
  • Аналогичным образом, оставляя по умолчанию разделитель TPL, как правило, достаточно хорош, чтобы разбить DataRows на все задачи.
  • Для каждой задачи потребуется отдельное Sql-соединение.
  • Вместо того, чтобы создавать и удалять команду для каждого вызова, создайте его один раз для каждой задачи, а затем снова используйте одну и ту же команду, просто обновляя параметры каждый раз.
  • Используйте LocalInit / Local Last lambdas для настройки и очистки задачи, например, для удаления команд и соединений.
  • Вы также можете рассмотреть возможность использования .Prepare() если вы используете версии AdHoc Sql или Sql до 2005 года
  • Я предполагаю, что перечисление строк DataTable's является streamобезопасным. Конечно, вы захотите дважды проверить это.

Примечание:

10 минут для 3000 рядов чрезмерны даже с широким столом и одной нитью. Что делает ваш пророк? Я предположил, что обработка не является тривиальной, отсюда и необходимость в SPROC, но если вы просто делаете простые вставки, как следует из комментария @ 3dd, SqlBulkCopy будет давать вставки ~ 1M строк в минуту на достаточно узкой таблице.

лучше передать всю таблицу данных в базу данных

 obj_AseCommand.CommandText = sql_proc; obj_AseCommand.CommandType = CommandType.StoredProcedure; obj_AseCommand.Connection = db_Conn; obj_AseCommand.Connection.Open(); obj_AseCommand.Parameters.AddWithValue("@Parametername",DataTable); obj_AseCommand.ExecuteNonQuery(); 

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

 CREATE TYPE EmpType AS TABLE ( ID INT, Name VARCHAR(3000), Address VARCHAR(8000), Operation SMALLINT //your columns ) 

в магазине вы можете сделать что-то вроде этого …

 create PROCEDURE demo @Details EmpType READONLY // it must be read only AS BEGIN insert into yourtable //insert data select * from @Details END 

Вы можете использовать SqlBulkCopy . См. Пример кода ниже. Метод WriteToServer записывает данные, datatable к базе данных, при условии, что они имеют одинаковое сопоставление

 using (SqlBulkCopy bulkCopy = new SqlBulkCopy(ConSQL)) { if (ConSQL.State == ConnectionState.Closed) { ConSQL.Open(); } bulkCopy.ColumnMappings.Add(0, 0); bulkCopy.ColumnMappings.Add(1, 1); bulkCopy.ColumnMappings.Add(2, 2); bulkCopy.DestinationTableName = "dbo.TableName"; bulkCopy.WriteToServer(dataTable); bulkCopy.Close(); //redundant - since using will dispose the object } 

Вы можете использовать SqlBulkCopy

руководство здесь