【问题标题】:Convert C# SQL Loop to Linq将 C# SQL 循环转换为 Linq
【发布时间】:2014-10-06 13:27:17
【问题描述】:

我有一个名为 ListTypes 的列表,其中包含 10 种产品。下面的存储过程循环并获取正在循环的产品的每条记录,并将其存储在 ListIds 列表中。这正在杀死我的 sql 框,因为我有超过 200 个用户整天不断地执行它。

我知道循环 sql 语句不是一个好的架构,但这是我让它工作的唯一方法。有什么想法我可以在不循环的情况下做到这一点吗?也许是一个 Linq 声明,我从来没有使用过这么大的 Linq。谢谢。

protected void GetIds(string Type, string Sub)
{
   LinkedIds.Clear();

  using (SqlConnection cs = new SqlConnection(connstr))
  {
    for (int x = 0; x < ListTypes.Count; x++)
    {
        cs.Open();
        SqlCommand select = new SqlCommand("spUI_LinkedIds", cs);
        select.CommandType = System.Data.CommandType.StoredProcedure;
        select.Parameters.AddWithValue("@Type", Type);
        select.Parameters.AddWithValue("@Sub", Sub);
        select.Parameters.AddWithValue("@TransId", ListTypes[x]);

        SqlDataReader dr = select.ExecuteReader();

        while (dr.Read())
        {
            ListIds.Add(Convert.ToInt32(dr["LinkedId"]));
        }
        cs.Close();
    }
  }
}

【问题讨论】:

  • 列表中总是10个项目吗?
  • @DavidG 是动态的,所以它可以是 10、15 或 8。
  • 为什么不在数据集中有这个并将数据集对象分配给 var 对象?或者您也可以继续使用 datareader 并将 datareader 对象分配给 var 对象var dr= select.ExecuteReader();
  • 一点优化:在循环之外创建 SqlCommand 及其参数一次。然后在循环内设置这些参数的值并执行。在整个循环中保持连接打开。
  • 将 ListTypes 放入数据表并(一次)传递给期望表变量的 sp? (codeproject.com/Tips/214492/…)

标签: c# sql performance linq


【解决方案1】:

不是一个完整的答案,但这不适合评论。您至少可以像这样更新现有代码以提高效率:

protected List<int> GetIds(string Type, string Sub, IEnumerable<int> types)
{
    var result = new List<int>();

    using (SqlConnection cs = new SqlConnection(connstr))
    using (SqlCommand select = new SqlCommand("spUI_LinkedIds", cs))
    {
        select.CommandType = System.Data.CommandType.StoredProcedure;
        //Don't use AddWithValue! Be explicit about your DB types
        // I had to guess here. Replace with the actual types from your database
        select.Parameters.Add("@Type", SqlDBType.VarChar, 10).Value = Type;
        select.Parameters.Add("@Sub", SqlDbType.VarChar, 10).Value = Sub;
        var TransID = select.Parameters.Add("@TransId", SqlDbType.Int);
        cs.Open();

        foreach(int type in types)
        {
            TransID.Value = type;
            SqlDataReader dr = select.ExecuteReader();
            while (dr.Read())
            {
                result.Add((int)dr["LinkedId"]);
            }
        }
    }
    return result;
}

请注意,这种方式您只需打开和关闭一次连接。通常在 ADO.Net 中,最好使用新连接并为每个查询重新打开它。例外是在这样的紧密循环中。此外,以这种方式在循环内唯一改变的是一个参数值。最后,最好设计不依赖其他类状态的方法。此方法不再需要了解 ListTypesListIds 类变量,这使得(除其他外)可以对方法进行更好的单元测试。

同样,这不是一个完整的答案;这只是一个渐进式的改进。您真正需要做的是编写另一个接受表值参数的存储过程,并在现有存储过程的查询的基础上使用表值参数连接,以便所有这些都适合单个 SQL 语句。但是,在您分享您的存储过程代码之前,我可以为您提供尽可能多的帮助。

【讨论】:

  • var TransID = select.Parameters.Add("@TransId", SqlDbType.Int);这必须通过 ListTypes 列表,数字总是在变化。
  • 这个想法是假设我有一个表,我必须从包含这 10 种产品的表中获取所有记录。我怎样才能获得所有这些产品?但这个数字是动态的。
  • @Apollo 您会将列表类型作为参数传递,如果您继续阅读此代码,则会在每次迭代时从参数为循环内的参数分配一个值,
  • 您确定关闭和打开连接有问题吗?我相信连接池会处理它。
  • @SriramSakthivel 通常是这样。但在一个紧密的循环中,这会更有效率。
【解决方案2】:

除了其他人写的改进。 您可以将您的 ID 插入到临时表中,然后制作一个

SELECT * from WhatEverTable WHERE transid in (select transid from #tempTable)

在 MSSQL 上,这运行得非常快。

当您不使用 MSSQL 时,一个很棒的带有连接的 SQL-Select 可能比 SELECT IN 更快。您必须自己在 DBMS 上测试这些案例。

【讨论】:

    【解决方案3】:

    根据您的评论:
    The idea is lets say I have a table and I have to get all records from the table that has this 10 types of products. How can I get all of this products? But this number is dynamic.

    那么...为什么要使用存储过程呢?为什么不查询表?

    //If [Type] and [Sub] arguments are external inputs - as in, they come from a user request or something - they should be sanitized. (remove or escape '\' and apostrophe signs)
    //create connection
    
    string queryTmpl = "SELECT LinkedId FROM [yourTable] WHERE [TYPE] = '{0}' AND [SUB] = '{1}' AND [TRANSID] IN ({2})";
    string query = string.Format(queryTmpl, Type, Sub, string.Join(", ", ListTypes);
    SqlCommand select = new SqlCommand(query, cs);
    
    //and so forth
    

    要使用 Linq-to-SQL,您需要将表映射到一个类。这将使查询更易于执行。

    【讨论】:

    • 是的,假设我想获取汽车、自行车、车轮等的所有记录。这可能是动态的,我需要将它们添加到列表中。我不想一个一个循环并将它们存储到一个列表中。
    • @Apollo 这不会循环。这只是转到“yourTable”并返回“WHERE”子句为真的所有记录。然后每个用户将只向数据库发出一个请求,而不是 ListTypes.length 次数
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-05-10
    • 1970-01-01
    • 2020-04-24
    • 1970-01-01
    • 1970-01-01
    • 2014-03-16
    • 2012-08-10
    相关资源
    最近更新 更多