【问题标题】:Reading from HUGE database table从 HUGE 数据库表中读取
【发布时间】:2011-07-25 13:00:18
【问题描述】:

我正在使用 LINQ 访问我的数据库表。有些表可能有(数十万)条记录。

使用如下语句从表中读取:

var records = db.Logs;

会使应用程序非常非常慢。它将一次加载所有记录。

我需要用多个条件过滤结果,例如:

if (UserID != null)
{
    records = records.Where(r => r.User == UserID);
}

if (UserIP != null)
{
    records = records.Where(r => r.IP   == UserIP);
}

问题是我第一次从表中读取会带来所有记录,并且会使应用程序非常慢。

有什么方法可以像我们过去对 SQL 语句所做的那样在 LINQ 语句(if、switch)中创建条件?

这是旧逻辑的创建方式:

string sql = "SELECT * FROM Log WHERE 1=1";
if (UserID != null)
{
    sql += " AND User = '" + UserID + "'";
}

if (UserIP != null)
{
    sql += " AND IP = '" + UserIP + "'";
}

sqlCmd.query(sql);

【问题讨论】:

  • 您是否在自己的线程上运行此查询并使用回调来获取结果?
  • @Hamid,您使用的是什么 ORM?实体框架? LINQ 到 SQL?这两者都会生成看起来像您的“旧逻辑”的智能 TSQL,除非采取措施防止它发生,例如从 IQueryable 强制转换为 IEnumerableToList()
  • @Hamid,如果可能,请启动 SQL Profiler,这样您就可以查看服务器上实际运行的查询。这将教你很多关于 LINQ、EF 的知识,并允许你进行调优。

标签: c# asp.net asp.net-mvc linq asp.net-mvc-3


【解决方案1】:

其实是这样的赋值:

  var records = db.Logs;

不会立即执行查询。它只准备一个数据结构以供以后使用。只有在代码需要数据时才会执行查询,并且任何 where 子句都集成在查询中,从而阻止它返回整个表。

完全可以做这样的事情:

  var records = db.Logs;

  if (filter1) records = records.Where(r => r.Field1 == condition1);
  if (filter2) records = records.Where(r => r.Field2 == condition2);

这将在执行一个查询时结束,使用(某种)动态 where 语句。

但是,您应该定义正确的索引。

【讨论】:

  • 我们再次看到:不要将性能问题归咎于某个特定陈述,除非您已进行测量以验证这一说法:var records = db.Logs; 已被证明是无辜的。
【解决方案2】:

我认为你错了。对 db.Logs 的调用应该返回一个 IQueryable - 这意味着在需要检索数据之前不会执行查询。例如访问 Log 类的属性或者将集合转换为列表 .ToList()

【讨论】:

    【解决方案3】:

    db.Logs 是什么?不是IEnumerable<T> 还是IQueryable<T>?通常在 Linq2SQL 中,只有调用 .ToArray.ToList 才会执行查询,因此您可以先构建查询树。

    【讨论】:

      【解决方案4】:

      问题是我第一次从表中读取会带来所有记录,并且会使应用程序非常慢。

      var records = db.Logs
      

      不从表中读取。除了创建查询之外,您什么都没做(这类似于创建 SQL 命令文本而不将命令文本发送到数据库以执行)。打开让您看到正在发送到数据库的 SQL 命令的功能,您将看到这行代码不会向数据库发送任何内容。事实上,这一行也没有:

      records = records.Where(r => r.User == UserID);
      

      这只是修改名为records 的查询(您应该真正将其称为recordsQuery)以在User 上添加一个条件。直到您对查询进行迭代,它才会真正发送到数据库执行。所以要么

      records.ToList();
      

      foreach(var record in records) {
          // something something
      }
      

      或许多其他执行查询的方式。

      这是旧逻辑的创建方式:

      我希望不会。你好注入攻击!

      有些表可能有(数十万)条记录。

      数十万不算什么。

      【讨论】:

        【解决方案5】:

        最好使用分页,让你的数据做任何你想做的事,然后再获得另一个页面。

        【讨论】:

          【解决方案6】:

          试试这个:

          List<Records> records;
          
          if (UserID != null)
          {
              records = db.Logs.Where(r => r.User == UserID).ToList();
          }
          else
          {
              records = db.Logs.ToList();
          }
          

          【讨论】:

            【解决方案7】:

            假设您使用的是 SQL Server,数十万行远非巨大。

            回到主题,如果你传递一个IQueryable&lt;T&gt;,那么每次枚举时都会执行SQL。如果你的旧代码是正确的,而你的新代码很慢,这很可能是你的问题。您可以通过调用ToList(),将其转换为IEnumerable&lt;T&gt; 并将所有数据放入内存来避免这种情况。

            最后,您可以通过调用.Skip(PageSize * PageIndex).Take(PageSize)来分页数据。

            【讨论】:

              【解决方案8】:

              如果你使用 lambda 表达式,查询在你使用它之前不会执行,例如转换为列表或枚举所有数据。

              var filtered = (from l in db.Logs
              where l => l.User == UserID
              select l).ToList();
              

              【讨论】:

              • 请先试一下再说,谢谢。如果您真的关心性能,请不要使用 linq。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2020-03-13
              • 2018-03-21
              • 1970-01-01
              • 1970-01-01
              • 2018-02-17
              • 1970-01-01
              相关资源
              最近更新 更多