2008-10-16 22 views
50

Tôi thường phải tải nhiều mục vào một bản ghi cụ thể trong cơ sở dữ liệu. Ví dụ: một trang web hiển thị các mục để bao gồm cho một báo cáo, tất cả đều là các bản ghi trong cơ sở dữ liệu (Báo cáo là một bản ghi trong bảng Báo cáo, Các mục là các bản ghi trong bảng Mục). Người dùng đang chọn các mục để bao gồm trong một báo cáo qua ứng dụng web và giả sử họ chọn 3 mục và gửi. Quá trình sẽ thêm 3 mục này vào báo cáo này bằng cách thêm các bản ghi vào một bảng gọi là ReportItems (ReportId, ItemId).Danh sách truyền <> đến thủ tục lưu sẵn SQL

Hiện nay, tôi sẽ làm một cái gì đó như thế này trong trong các mã:

public void AddItemsToReport(string connStr, int Id, List<int> itemList) 
{ 
    Database db = DatabaseFactory.CreateDatabase(connStr); 

    string sqlCommand = "AddItemsToReport" 
    DbCommand dbCommand = db.GetStoredProcCommand(sqlCommand); 

    string items = ""; 
    foreach (int i in itemList) 
     items += string.Format("{0}~", i); 

    if (items.Length > 0) 
     items = items.Substring(0, items.Length - 1); 

    // Add parameters 
    db.AddInParameter(dbCommand, "ReportId", DbType.Int32, Id); 
    db.AddInParameter(dbCommand, "Items", DbType.String, perms); 
    db.ExecuteNonQuery(dbCommand); 
} 

và điều này trong thủ tục lưu trữ:

INSERT INTO ReportItem (ReportId,ItemId) 
SELECT @ReportId, 
      Id 
FROM  fn_GetIntTableFromList(@Items,'~') 

đâu hàm trả về một bảng một cột các số nguyên.

Câu hỏi của tôi là: có cách nào tốt hơn để xử lý một cái gì đó như thế này? Lưu ý, tôi không hỏi về việc chuẩn hóa cơ sở dữ liệu hay bất cứ thứ gì như thế, câu hỏi của tôi liên quan cụ thể đến mã.

Trả lời

5

Bạn có thể làm những gì bạn đã có, vượt qua trong một chuỗi phân và sau đó phân tích ra một giá trị bảng, hoặc lựa chọn nào khác là đi qua trong một wodge của XML và kinda nhiều giống nhau:

http://weblogs.asp.net/jgalloway/archive/2007/02/16/passing-lists-to-sql-server-2005-with-xml-parameters.aspx

Tôi chưa có cơ hội xem SQL 2008 để xem liệu họ có thêm bất kỳ chức năng mới nào để xử lý loại điều này không.

19

chuỗi bạn tham gia Logic có lẽ có thể được đơn giản hóa:

string items = 
    string.Join("~", itemList.Select(item=>item.ToString()).ToArray()); 

Điều đó sẽ giúp bạn tiết kiệm một số chuỗi nối, đó là tốn kém trong Net.

Tôi không nghĩ có gì sai với cách bạn đang lưu các mục. Bạn đang hạn chế các chuyến đi đến db, đó là một điều tốt. Nếu cấu trúc dữ liệu của bạn phức tạp hơn một danh sách các int, tôi sẽ đề xuất XML.

Lưu ý: Tôi đã được hỏi trong phần nhận xét nếu điều này có thể giúp chúng tôi lưu bất kỳ chuỗi ký tự nào (nó được đưa vào). Tôi nghĩ đó là một câu hỏi tuyệt vời và muốn theo dõi điều đó.

Nếu bạn bóc mở chuỗi.Jinin với Reflector bạn sẽ thấy rằng Microsoft đang sử dụng một vài kỹ thuật không an toàn (trong .Net), bao gồm sử dụng con trỏ char và cấu trúc được gọi là UnSafeCharBuffer. Những gì họ đang làm, khi bạn thực sự đun sôi nó xuống, đang sử dụng con trỏ để đi qua một chuỗi rỗng và xây dựng sự tham gia. Hãy nhớ rằng lý do chính chuỗi nối là rất tốn kém trong. Net là một đối tượng chuỗi mới được đặt trên heap cho mỗi nối, bởi vì chuỗi là bất biến. Những hoạt động bộ nhớ này đắt tiền. String.Join (..) về cơ bản là phân bổ bộ nhớ một lần, sau đó hoạt động trên nó với một con trỏ. Rất nhanh.

+0

Đó là thực sự sẽ giúp bạn tiết kiệm từ nối? Bạn có biết liệu phương pháp này có được triển khai bằng StringBuilder không? –

+0

Có, nó sẽ giúp bạn tiết kiệm từ nối. String.Join sử dụng UnsafeCharBuffer để làm concat (xem nó trong Reflector). Theo như LINQ đi, đó chỉ là liệt kê trên danh sách gọi ToString(), đó là không thể tránh khỏi, và tạo ra một mảng. Việc tạo mảng có thể tốn kém. –

+0

Tôi không biết rằng bạn có thể làm điều đó như vậy, cảm ơn cho tip! –

8

Một vấn đề tiềm ẩn với kỹ thuật của bạn là nó không xử lý danh sách rất lớn - bạn có thể vượt quá độ dài chuỗi tối đa cho cơ sở dữ liệu của bạn.Tôi sử dụng một phương pháp helper rằng concatenates các giá trị số nguyên vào một đếm chuỗi, mỗi trong số đó là ít hơn mức tối đa quy định (thực hiện sau đây cũng kiểm tra tùy chọn và loại bỏ các bản sao id):

public static IEnumerable<string> ConcatenateValues(IEnumerable<int> values, string separator, int maxLength, bool skipDuplicates) 
{ 
    IDictionary<int, string> valueDictionary = null; 
    StringBuilder sb = new StringBuilder(); 
    if (skipDuplicates) 
    { 
     valueDictionary = new Dictionary<int, string>(); 
    } 
    foreach (int value in values) 
    { 
     if (skipDuplicates) 
     { 
      if (valueDictionary.ContainsKey(value)) continue; 
      valueDictionary.Add(value, ""); 
     } 
     string s = value.ToString(CultureInfo.InvariantCulture); 
     if ((sb.Length + separator.Length + s.Length) > maxLength) 
     { 
      // Max length reached, yield the result and start again 
      if (sb.Length > 0) yield return sb.ToString(); 
      sb.Length = 0; 
     } 
     if (sb.Length > 0) sb.Append(separator); 
     sb.Append(s); 
    } 
    // Yield whatever's left over 
    if (sb.Length > 0) yield return sb.ToString(); 
} 

Sau đó, bạn sử dụng nó một cái gì đó như:

using(SqlCommand command = ...) 
{ 
    command.Connection = ...; 
    command.Transaction = ...; // if in a transaction 
    SqlParameter parameter = command.Parameters.Add("@Items", ...); 
    foreach(string itemList in ConcatenateValues(values, "~", 8000, false)) 
    { 
     parameter.Value = itemList; 
     command.ExecuteNonQuery(); 
    } 
} 
+1

Điểm tốt, tôi đã không nghĩ đến việc vượt quá độ dài chuỗi. Mặc dù tôi nghi ngờ tôi sẽ có trường hợp thậm chí đến gần, thật tốt khi biết điều này, cảm ơn! –

34

Nếu đi đến SQL Server 2008 là một lựa chọn cho bạn, có một tính năng mới gọi là "Bảng giá trị các thông số" để giải quyết này vấn đề chính xác.

Xem thêm chi tiết về TVP herehere hoặc chỉ yêu cầu Google cho "tham số giá trị bảng SQL Server 2008" - bạn sẽ tìm thấy nhiều thông tin và mẫu.

cao đề nghị - nếu bạn có thể di chuyển đến SQL Server 2008 ...

5

Tại sao không sử dụng một tham số bảng có giá trị? http://msdn.microsoft.com/en-us/library/bb675163.aspx

+0

Vì yêu cầu loại bảng do người dùng xác định, có thể nằm trong db vĩnh viễn trừ khi bạn đảm bảo rằng bạn xóa loại này sau mỗi lần gọi. – alpav

+0

Đó có phải là vấn đề không? – GaTechThomas

+0

Chúng rất khó quản lý, nếu bạn phải thay đổi tập lệnh thành triển khai trực tiếp chẳng hạn. – philw

Các vấn đề liên quan