[英]Passing List<> to SQL Stored Procedure
我經常不得不將多個項目加載到數據庫中的特定記錄。 例如:網頁顯示要包含在單個報告中的項目,所有項目都是數據庫中的記錄(“報告”是“報告”表中的記錄,“項目”是“項目”表中的記錄)。 用戶正在通過網絡應用程序選擇要包含在單個報告中的項目,假設他們選擇了3個項目並提交。 該過程將通過將記錄添加到稱為ReportItems(ReportId,ItemId)的表中來將這3個項目添加到此報告中。
目前,我將在代碼中執行以下操作:
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);
}
在存儲過程中:
INSERT INTO ReportItem (ReportId,ItemId)
SELECT @ReportId,
Id
FROM fn_GetIntTableFromList(@Items,'~')
該函數返回一列整數表。
我的問題是:有沒有更好的方法來處理這樣的事情? 注意,我不是在問數據庫規范化之類的問題,我的問題專門與代碼有關。
您的字符串連接邏輯可能可以簡化:
string items =
string.Join("~", itemList.Select(item=>item.ToString()).ToArray());
這樣可以節省一些字符串連接,在.Net中這是昂貴的。
我認為您保存項目的方式沒有任何問題。 您限制了對數據庫的訪問,這是一件好事。 如果您的數據結構比整數列表更復雜,我建議使用XML。
注意:我在評論中被問到這是否可以節省任何字符串連接(的確如此)。 我認為這是一個很好的問題,並希望對此進行跟進。
如果剝開字符串。與Reflector結合使用,您會看到Microsoft正在使用幾種不安全的技術(按.Net的說法),包括使用char指針和稱為UnSafeCharBuffer的結構。 當您真正將其歸結時,他們正在做的事情是使用指針遍歷一個空字符串並建立連接。 請記住,.Net中字符串連接如此昂貴的主要原因是,每個字符串都會在堆上放置一個新的字符串對象,因為字符串是不可變的。 這些存儲器操作是昂貴的。 String.Join(..)本質上是分配一次內存,然后使用指針對其進行操作。 非常快。
該技術的一個潛在問題是它不能處理非常大的列表-您可能超過了數據庫的最大字符串長度。 我使用了一個輔助方法,該方法將整數值連接到一個字符串枚舉中,每個字符串小於指定的最大值(以下實現也可以選擇檢查並刪除重復的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();
}
然后,您可以使用類似:
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();
}
}
您可以執行已有的操作,傳入一個定界的字符串,然后解析為表值,或者另一種選擇是傳入大量的XML,大致相同:
我還沒有機會看一下SQL 2008,看看他們是否添加了任何新功能來處理這種事情。
有關此問題以及可以使用的不同方法的詳細討論,請參見http://www.sommarskog.se/arrays-in-sql-2005.html 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.