为了账号安全,请及时绑定邮箱和手机立即绑定

将List <>传递给SQL存储过程

/ 猿问

将List <>传递给SQL存储过程

慕容3067478 2019-11-30 14:40:12

我经常不得不将多个项目加载到数据库中的特定记录。例如:网页显示要包含在单个报告中的项目,所有项目都是数据库中的记录(“报告”是“报告”表中的记录,“项目”是“项目”表中的记录)。用户正在通过网络应用程序选择要包含在单个报告中的项目,假设他们选择了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,'~')

该函数返回一列整数表。


我的问题是:有没有更好的方法来处理这样的事情?注意,我不是在问数据库规范化之类的问题,我的问题专门与代码有关。


查看完整描述

3 回答

?
拉风的咖菲猫

如果您选择使用SQL Server 2008,则有一个名为“表值参数”的新功能可以解决此确切问题。


在此处和此处查看有关TVP的更多详细信息,或者只是向Google询问“ SQL Server 2008表值参数”-您会发现大量信息和示例。


强烈建议- 如果可以迁移到SQL Server 2008 ...


查看完整回答
反对 2019-11-30
?
FFIVE

您的字符串连接逻辑可能可以简化:


string items = 

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

这样可以节省一些字符串连接,在.Net中这是昂贵的。


我认为您保存项目的方式没有任何问题。您限制了对数据库的访问,这是一件好事。如果您的数据结构比整数列表更复杂,我建议使用XML。


注意:我在评论中被问到这是否可以节省任何字符串连接(的确如此)。我认为这是一个很好的问题,并希望对此进行跟进。


如果剥开字符串。与Reflector结合使用,您会看到Microsoft正在使用几种不安全的(按.Net的说法)技术,包括使用char指针和称为UnSafeCharBuffer的结构。当您真正将其归结时,他们正在做的事情是使用指针遍历一个空字符串并建立连接。请记住,.Net中字符串连接如此昂贵的主要原因是,每个字符串都会在堆上放置一个新的字符串对象,因为字符串是不可变的。这些存储器操作是昂贵的。String.Join(..)本质上是分配一次内存,然后使用指针对其进行操作。非常快。


查看完整回答
反对 2019-11-30
?
陪伴而非守候

该技术的一个潜在问题是它不能处理非常大的列表-您可能超过了数据库的最大字符串长度。我使用了一个辅助方法,该方法将整数值连接到一个字符串枚举中,每个字符串都小于指定的最大值(以下实现也可以选择检查并删除重复的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();

    }

}


查看完整回答
反对 2019-11-30

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信