2015-01-05 2 views
4

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

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 строк.

+1

Просто из любопытства - почему вы добавления 3000 строк в базу данных с помощью хранимой процедуры? если это из какого-либо входного файла, почему бы не импортировать его непосредственно в базу данных с помощью какого-либо инструмента управления? – ShayD

+0

Данные о данных заполняются из другой базы данных - основной базы данных. Моя цель - извлечь данные из основной базы данных и вставить их в мою базу данных. Хранимая процедура используется для вставки данных в мою базу данных. – Harsh

+3

Почему бы не использовать объемную вставку или сервер ссылок, а также получить SQL-выборку и вставить данные. – 3dd

ответ

8

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

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

Оригинал ответа, множественные параллельные вставки в базу данных

Вы можете сделать это параллельно с использованием TPL, например, особенно с перегрузкой localInitParallel.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 versions prior to 2005
  • Я предполагаю, перечислив DataTable's строк является поточно. Конечно, вы захотите дважды проверить это.

Сторона Примечание:

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

+0

Привет Стюарт, спасибо за вход. Я попробовал это, используя приведенный ниже код, используя AseBulkCopy, так как у нас есть база данных Sybase AseBulkCopy obj_AseBulkCopy = new AseBulkCopy (db_Conn); obj_AseBulkCopy.DestinationTableName = "db_table"; obj_AseBulkCopy.BatchSize = 1000; db_Conn.Open(); obj_AseBulkCopy.WriteToServer (дт); db_Conn.Close(); Однако все равно потребовалось то же самое время для выполнения. Я что-то пропустил в этом коде? – Harsh

+0

Что-то очень странное происходит с вашей РСУБД, если даже BulkCopy занимает 10 минут на 3000 строк. Я предполагаю, что у вас есть триггеры на вставленной таблице, которые делают много логики, или есть много конфликтов блокировок, или, возможно, у вас много ограничений, правил, индексов и таблицы чрезвычайно широки. Вам нужно, чтобы администратор базы данных серьезно посмотрел на таблицу - технологии вставки и отсутствие параллелизма не являются узким местом здесь :( – StuartLC

4

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

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 
0

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

руководство here

+0

Это скорее комментарий, чем ответ – 3dd

+0

Хорошо, извините, я новый пользователь :) –

+2

Это правильный ответ (хотя это может быть не очень хороший ответ), его можно улучшить, добавив некоторое объяснение, почему и как. –

3

Вы можете использовать SqlBulkCopy. См. Пример кода ниже. Метод WriteToServer, пишет The 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 

} 
Смежные вопросы