Каков наилучший способ вставки больших баз данных из С#?
Как я/что лучший способ делать массовые вставки базы данных?
В С# я выполняю итерацию по коллекции и вызываю хранимую процедуру вставки для каждого элемента в коллекции.
Как отправить все данные в один вызов базы данных?
например. У меня есть список людей (List<Person>
), содержащий 10 элементов. В настоящее время я вызываю файл InsertPerson 10 раз. Я хотел бы уменьшить это до 1 вызова.
Я использую MS SQL Server 2005.
Ответы
Ответ 1
CsharperGuyInLondon, вот простой пример кода SqlBulkCopy:
using System.Data.SqlClient;
DataTable table = new DataTable("States");
// construct DataTable
table.Columns.Add(new DataColumn("id_state", typeof(int)));
table.Columns.Add(new DataColumn("state_name", typeof(string)));
// note: if "id_state" is defined as an identity column in your DB,
// row values for that column will be ignored during the bulk copy
table.Rows.Add("1", "Atlanta");
table.Rows.Add("2", "Chicago");
table.Rows.Add("3", "Springfield");
using(SqlBulkCopy bulkCopy = new SqlBulkCopy(connectionString))
{
bulkCopy.BulkCopyTimeout = 600; // in seconds
bulkCopy.DestinationTableName = "state";
bulkCopy.WriteToServer(table);
}
Ответ 2
Ну, 10 предметов - это не то, что я называю массовым, но для больших наборов SqlBulkCopy
- ваш друг. Все, что вам нужно сделать, это подать его либо DataTable
, либо IDataReader
(мой предпочтительный вариант, потому что мне нравится потоковое API). Я сделал что-то подобное здесь (вы можете игнорировать сторону xml - просто подкласс SimpleDataReader).
Ответ 3
Класс .NET SqlBulkCopy работает очень хорошо.
Ответ 4
Вы можете создать BLOB (изображение) и отправить его в качестве параметра хранимой процедуры. Внутри хранимой процедуры вы можете получить все элементы, используя substring().
Ответ 5
Вот хороший пример SqlBulkCopy
в действии:
http://blogs.msdn.com/nikhilsi/archive/2008/06/11/bulk-insert-into-sql-from-c-app.aspx
Ответ 6
Я создаю список как строку xml и передаю его в сохраненный proc. В SQL 2005 у него есть расширенные функциональные возможности xml для синтаксического анализа xml и выполнения объемной вставки.
проверить этот пост:
Передача списков SQL Server 2005 с параметрами XML
Ответ 7
Сбросьте свои данные в ограничиваемый канал (или что-то еще, если ваши данные имеют в нем каналы), и используйте Bulk Insert.
Ответ 8
Вы можете обновить документ Xml, Sql 2005 отлично работает с ними. Один node для каждой строки, но только один параметр для Xml.
Ответ 9
Создайте XML-документ, содержащий все элементы, которые нужно вставить. Затем внутри хранимой процедуры используйте поддержку TSQL xml (OPENXML), чтобы прочитать все данные из документа XML и вставить его в свои таблицы с надеждой один оператор insert для каждой таблицы.
Однако, если вы только вставляете данные в одну таблицу и не нуждаетесь в логике на стороне базы данных, почему бы не использовать SqlBulkCopy?
Ответ 10
Верно решение для SqlBulkCopy, я создал класс, чем принимает Datatable
или List<T>
и размер буфера (CommitBatchSize
). Он преобразует список в таблицу данных с использованием расширения (во втором классе).
Он работает очень быстро. На моем ПК я могу вставить более 10 миллионов сложных записей менее чем за 10 секунд.
Вот класс:
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DAL
{
public class BulkUploadToSql<T>
{
public IList<T> InternalStore { get; set; }
public string TableName { get; set; }
public int CommitBatchSize { get; set; }=1000;
public string ConnectionString { get; set; }
public void Commit()
{
if (InternalStore.Count>0)
{
DataTable dt;
int numberOfPages = (InternalStore.Count / CommitBatchSize) + (InternalStore.Count % CommitBatchSize == 0 ? 0 : 1);
for (int pageIndex = 0; pageIndex < numberOfPages; pageIndex++)
{
dt= InternalStore.Skip(pageIndex * CommitBatchSize).Take(CommitBatchSize).ToDataTable();
BulkInsert(dt);
}
}
}
public void BulkInsert(DataTable dt)
{
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
// make sure to enable triggers
// more on triggers in next post
SqlBulkCopy bulkCopy =
new SqlBulkCopy
(
connection,
SqlBulkCopyOptions.TableLock |
SqlBulkCopyOptions.FireTriggers |
SqlBulkCopyOptions.UseInternalTransaction,
null
);
// set the destination table name
bulkCopy.DestinationTableName = TableName;
connection.Open();
// write the data in the "dataTable"
bulkCopy.WriteToServer(dt);
connection.Close();
}
// reset
//this.dataTable.Clear();
}
}
public static class BulkUploadToSqlHelper
{
public static DataTable ToDataTable<T>(this IEnumerable<T> data)
{
PropertyDescriptorCollection properties =
TypeDescriptor.GetProperties(typeof(T));
DataTable table = new DataTable();
foreach (PropertyDescriptor prop in properties)
table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
foreach (T item in data)
{
DataRow row = table.NewRow();
foreach (PropertyDescriptor prop in properties)
row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
table.Rows.Add(row);
}
return table;
}
}
}
Вот пример, когда я хочу вставить список моего пользовательского объекта List<PuckDetection>
(ListDetections
):
var objBulk = new BulkUploadToSql<PuckDetection>()
{
InternalStore = ListDetections,
TableName= "PuckDetections",
CommitBatchSize=1000,
ConnectionString="ENTER YOU CONNECTION STRING"
};
objBulk.Commit();