2009-03-25 54 views
29

Làm cách nào để/cách tốt nhất để thực hiện chèn cơ sở dữ liệu hàng loạt là gì?Cách tốt nhất để chèn cơ sở dữ liệu hàng loạt từ C# là gì?

Trong C#, tôi đang lặp qua bộ sưu tập và gọi thủ tục được lưu trữ chèn cho mỗi mục trong bộ sưu tập.

Làm cách nào để gửi tất cả dữ liệu trong một cuộc gọi cơ sở dữ liệu?

Ví dụ: nói rằng tôi có một danh sách người (List<Person>) chứa 10 mục. Tôi hiện đang gọi các InsertPerson được lưu trữ proc 10 lần. Tôi muốn giảm số này xuống 1 cuộc gọi.

Tôi đang sử dụng MS SQL Server 2005.

+0

Chắc chắn kiểm tra [ câu hỏi này] (http://stackoverflow.com/questions/629455/how-should-i-optimize-multiple-calls-in-my-net-code-to-a-trivial-stored-procedur). Câu trả lời topvoted (không, nó không phải của tôi: p) trình bày một giải pháp rất thanh lịch cho vấn đề chính xác này. – Brann

+0

cảm ơn bạn đã đặt câu hỏi này –

Trả lời

1

Dump dữ liệu của bạn vào một đường ống phân (hay cái gì khác nếu dữ liệu của bạn có đường ống trong nó) tập tin văn bản và sử dụng Bulk Insert.

+0

Phương pháp này cần phải được lập trình và nhanh chóng - không có đĩa i/o. Cảm ơn bạn. –

+0

Bạn có thể làm điều đó bằng cách sử dụng MemoryStream để tránh File I/O, nhưng bạn có thể chỉ cần thực hiện một SQLBulkCopy thực hiện boilerplate. Xem nhận xét của tôi cho câu trả lời của @Mark Gavell. –

2

Bạn có thể tạo BLOB (hình ảnh) và gửi nó làm tham số cho quy trình được lưu trữ. Bên trong thủ tục lưu sẵn, bạn có thể lấy tất cả các mục bằng cách sử dụng chuỗi con().

1

Bạn có thể cập nhật bằng tài liệu Xml, Sql 2005 hoạt động rất tốt với chúng. Một nút cho mỗi hàng, nhưng chỉ một tham số cho Xml.

24

Vâng, 10 mục không phải là thứ tôi gọi số lượng lớn, nhưng đối với các bộ lớn hơn, SqlBulkCopy là bạn của bạn. Tất cả những gì bạn cần làm là cấp dữ liệu theo số DataTable hoặc IDataReader (tùy chọn ưa thích của tôi, 'cos tôi thích API phát trực tuyến). Tôi đã làm một cái gì đó tương tự here (bạn có thể bỏ qua phía xml - chỉ cần phân lớp SimpleDataReader).

+0

Marc: bạn có biết những gì dưới mui xe của SqlBulkCopy? –

+0

+1 cho "truyền trực tuyến APIS". @ Jason, đoán của tôi là SqlBulkCopy kết thúc tốt đẹp câu lệnh chèn BULK: http://technet.microsoft.com/en-us/library/ms187042.aspx –

+0

Xin lỗi - là AFK - về cơ bản nó giống như API "bcp", v.v. - nhưng trực tiếp thông qua một luồng TDS (nó không bao gồm một exe, hoặc tương tự). –

1

Tạo một tài liệu XML chứa tất cả các mục sẽ được chèn vào. Sau đó, bên trong một thủ tục lưu sẵn, sử dụng hỗ trợ TSQL xml (OPENXML) để đọc tất cả dữ liệu từ tài liệu XML và chèn nó vào các bảng của bạn với hy vọng một câu lệnh chèn cho mỗi bảng.

Tuy nhiên, nếu bạn chỉ chèn dữ liệu vào một bảng và không cần bất kỳ logic bên cơ sở dữ liệu nào, tại sao không sử dụng SqlBulkCopy?

+0

Bạn không cần sử dụng OPENXML với SQL 2005, chỉ cần sử dụng kiểu dữ liệu XML dựng sẵn. –

+0

@Adam, OPENXML vẫn hữu ích nếu bạn chỉ sử dụng XML như một cách để vận chuyển hàng. Tuy nhiên, các kiểu dữ liệu XML của SQL 2005 rất hữu ích nếu bạn muốn lưu trữ XML trong cơ sở dữ liệu te. –

2

Tôi tạo danh sách dưới dạng chuỗi xml và chuyển nó vào proc được lưu trữ. Trong SQL 2005, nó đã nâng cao các chức năng xml để phân tích cú pháp xml và thực hiện chèn số lượng lớn.

kiểm tra bài đăng này: Passing lists to SQL Server 2005 with XML Parameters

26

CsharperGuyInLondon, đây là một ví dụ đơn giản mã 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); 
} 
+1

Tôi đánh giá cao mã mẫu này. Đã dành cho tôi rất nhiều thời gian. lặp đi lặp lại chèn 17k (32 cột mỗi hàng) bản ghi mất khoảng 200 giây trong cài đặt mạng của tôi. Sử dụng phương pháp này mất ít hơn 10 giây. –

+0

Một số suy nghĩ Bạn không còn cần phải tạo một đối tượng DataColoumn bạn có thể chuyển tên cột và gõ trực tiếp vào Add Function now Add ("id_state", typeof (int)); Ngoài ra, bố cục bảng cần khớp với bảng trong sql. Thứ tự cột là tốt – R2D2

1

Re giải pháp cho SqlBulkCopy, tôi tạo ra một lớp hơn mất Datatable hoặc một List<T> và một Buffer kích thước (CommitBatchSize). Nó sẽ chuyển đổi danh sách thành một bảng dữ liệu bằng cách sử dụng một phần mở rộng (trong lớp thứ hai).

Nó hoạt động rất nhanh. Trên PC của tôi, tôi có thể chèn hơn 10 triệu bản ghi phức tạp trong vòng chưa đến 10 giây.

Đây là lớp:

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; 
    } 
} 

}

Dưới đây là một ví dụ khi tôi muốn chèn một danh sách các đối tượng tùy chỉnh của tôi List<PuckDetection> (ListDetections):

var objBulk = new BulkUploadToSql<PuckDetection>() 
{ 
     InternalStore = ListDetections, 
     TableName= "PuckDetections", 
     CommitBatchSize=1000, 
     ConnectionString="ENTER YOU CONNECTION STRING" 
}; 
objBulk.Commit(); 
Các vấn đề liên quan