حسین احمدی
بنیانگذار توسینسو و برنامه نویس و توسعه دهنده ارشد وب

استفاده از کلاس SqlBulkCopy در سی شارپ

عملیات درج رکورد ها در SQL Server به تعداد بالا می تونه خیلی زمان بر باشه، البته اگر به صورت عادی این کار انجام بشه. اما بوسیله قابلیت Bulk Insert یا درج انبوه رکوردها مدت زمان این کار به طرز چشم گیری کاهش پیدا می کنه. برای مثال، ممکنه شما قصد داشته باشید 1 میلیون رکورد رو در بانکتون INSERT کنید، گر تمام این رکورد ها رو خط به خط اجرا کنید این عملیات ممکنه ساعت ها طول بکشه، در حالی که بوسیله Bulk Insert این زمان حتی تا کمتر از 1 دقیقه هم کاهش پیدا می کنه. در این مطلب قصد داریم با نحوه Bulk Insert در SQL Server با کمک کلاس های DataTable و SqlBulkCopy آشنا بشیم.در ابتدا یک بانک و جدول نمونه با اسکریپت زیر ایجاد می کنیم:

دوره های شبکه، برنامه نویسی، مجازی سازی، امنیت، نفوذ و ... با برترین های ایران
create database SampleForBulkInsert;
go

create table SampleTable
(
 [Id] int identity not null primary key,
 [From] nvarchar(200) not null,
 [To] nvarchar(200) not null,
 [Date] datetime,
 [Balance] decimal not null
);
go

قطعه کد زیر عملیات درج رو به صورت رکورد به رکورد و استفاده مستقیم دستور INSERT INTO انجام میده. یعنی عملیات Bulk Insert نداریم:

Stopwatch sw = new Stopwatch();
sw.Start();
using (var cnn = new SqlConnection("data source=.; initial catalog=SampleForBulkInsert; user id=sa; password=1"))
{
    cnn.Open();
    for (int i = 0; i < 100000; i++)
    {
        var command = cnn.CreateCommand();
        command.CommandText = "insert into SampleTable values ('Hossein Ahmadi','Mohammad Nasiri',@date,10000);";
        command.Parameters.Add(new SqlParameter("date", DateTime.UtcNow));
        command.ExecuteNonQuery();
    }
}
sw.Stop();

Console.WriteLine(sw.Elapsed);

ما اینجا از کلاس StopWatch برای بدست آوردن مدت زمان اجرای کد استفاده کردیم که مدت زمان اجرا برای 100000 رکورد حدود 25 ثانیه بود:

00:00:25.7867841
Press any key to continue . . .

حالا عملیات درج رو برای 100000 رکورد با کلاس SqlBulkCopy و DataTable انجام میدیم:

Stopwatch sw = new Stopwatch();
sw.Start();

DataTable table = new DataTable();
table.Columns.Add("From", typeof(string));
table.Columns.Add("To", typeof(string));
table.Columns.Add("Date", typeof(DateTime));
table.Columns.Add("Balance", typeof(decimal));

for (int i = 0; i < 100000; i++)
{
    var dataRow = table.NewRow();
    dataRow["From"] = "Hosein Ahmadi";
    dataRow["To"] = "Mohammad Nasiri";
    dataRow["Date"] = DateTime.UtcNow;
    dataRow["Balance"] = 10000;
    table.Rows.Add(dataRow);
}

using (var cnn = new SqlConnection("data source=.; initial catalog=SampleForBulkInsert; user id=sa; password=1"))
{
    SqlBulkCopy bulkCopy = new SqlBulkCopy(cnn);
    bulkCopy.ColumnMappings.Add("From", "From");
    bulkCopy.ColumnMappings.Add("To", "To");
    bulkCopy.ColumnMappings.Add("Date", "Date");
    bulkCopy.ColumnMappings.Add("Balance", "Balance");
    bulkCopy.DestinationTableName = "SampleTable";
    cnn.Open();
    bulkCopy.WriteToServer(table);
}
sw.Stop();

Console.WriteLine(sw.Elapsed);

مدت زمان اجرای کوئری بالا خیلی سریعتر از حالت درج تک به تک رکورد ها هست، حدود نیم ثانیه که نسبت به 25 ثانیه تفاوت چشم گیری داره:

00:00:00.5161634
Press any key to continue . . .

و درج حدود 1 میلیون رکورد:

00:00:05.8161634
Press any key to continue . . .

اما در کد بالا به ترتیب چه کاری انجام دادیم، در ابتدا تعریف یک DataTable که شامل ستون هایی معادل جدول ما در بانک اطلاعاتی هست:

DataTable table = new DataTable();
table.Columns.Add("From", typeof(string));
table.Columns.Add("To", typeof(string));
table.Columns.Add("Date", typeof(DateTime));
table.Columns.Add("Balance", typeof(decimal));

در مرحله بعد اضافه کردن رکورد ها به DataTable:

for (int i = 0; i < 1000000; i++)
{
    var dataRow = table.NewRow();
    dataRow["From"] = "Hosein Ahmadi";
    dataRow["To"] = "Mohammad Nasiri";
    dataRow["Date"] = DateTime.UtcNow;
    dataRow["Balance"] = 10000;
    table.Rows.Add(dataRow);
}

و در نهایت درج رکورد ها در بانک اطلاعاتی:

using (var cnn = new SqlConnection("data source=.; initial catalog=SampleForBulkInsert; user id=sa; password=1"))
{
    SqlBulkCopy bulkCopy = new SqlBulkCopy(cnn);
    bulkCopy.ColumnMappings.Add("From", "From");
    bulkCopy.ColumnMappings.Add("To", "To");
    bulkCopy.ColumnMappings.Add("Date", "Date");
    bulkCopy.ColumnMappings.Add("Balance", "Balance");
    bulkCopy.DestinationTableName = "SampleTable";
    cnn.Open();
    bulkCopy.WriteToServer(table);
}

دو نکته که باید در کد بالا بهش توجه کنیم:

  1. اضافه کردن ColumnMapping برای مشخص کردن ستون های معادل در بانک اطلاعاتی
  2. مشخص کردن نام جدول بوسیله DestinationTableName

حسین احمدی
حسین احمدی

بنیانگذار توسینسو و برنامه نویس و توسعه دهنده ارشد وب

حسین احمدی ، بنیانگذار TOSINSO ، توسعه دهنده وب و برنامه نویس ، بیش از 12 سال سابقه فعالیت حرفه ای در سطح کلان ، مشاور ، مدیر پروژه و مدرس نهادهای مالی و اعتباری ، تخصص در پلتفرم دات نت و زبان سی شارپ ، طراحی و توسعه وب ، امنیت نرم افزار ، تحلیل سیستم های اطلاعاتی و داده کاوی ...

نظرات