【问题标题】:How can I simplify (speed up) the selecting query from a database that contains more than 1 million records using LINQ如何使用 LINQ 从包含超过 100 万条记录的数据库中简化(加速)选择查询
【发布时间】:2017-12-30 11:44:59
【问题描述】:

我的应用程序中有两个模型:获取客户名称的客户端和获取有关购买信息的付款。结果,我得到了每个客户在时间间隔内的购买清单 - fromDatetoDate。但是所有这些过程都需要太多时间。因为客户的数据库大约有 1.500 条记录并且付款 = 50 万。那么如何加快这个过程呢?

public async Task<List<SomeModel>> SomeMethod(DateTime? fromDate, DateTime? toDate)
{
    var clients = await _db.Clients.ToListAsync();

    var totals = new List<SomeModel>();

    foreach (var client in clients)
    {
        var payment = await _db.Payments.Where(pay => pay.ClientId == client.Id).Where(
            p =>
                DateTime.Compare(p.TradeDate, (DateTime)fromDate) >= 0 &&
                DateTime.Compare(p.TradeDate, (DateTime)toDate) <= 0).ToListAsync();
        var totalsByCust = new SomeModel{ Username = client.Username };
        foreach (var item in payment)
        {
            totalByCust.Bcf += item.Bcf;
            totalByCust.Ecn += item.Ecn;
            totalByCust.Ecbt += item.Ecbt;
            totalByCust.OpenGl += item.OpenGl;
            totalByCust.JeyK += item.JeyK;

        }
        totals.Add(totalByCust);
    }
    return totals;
}

【问题讨论】:

  • @rene 会发生什么?此过程将在服务器上花费相同的时间...(
  • 不,不会的,试试看。
  • 一个 SQL JOIN 将比向数据库服务器串行发送 1500 个查询更有效。
  • @rene 我的错,已编辑。
  • @不是。我相信的最后一次编辑:p

标签: c# asp.net sql-server entity-framework linq


【解决方案1】:

您应该使用存储过程而不是 Linq 查询。

【讨论】:

    【解决方案2】:

    组合查询可能是最直接的尝试:

    public async Task<List<SomeModel>> SomeMethod(DateTime? fromDate, DateTime? toDate) {
        var totals = new List<SomeModel>();
    
        var clientswithpayments = await _db.Clients.Select(client => new {
            client.Username,
            payments =  _db.Payments.Where(pay => pay.ClientId == client.Id).Where(p =>
                DateTime.Compare(p.TradeDate, (DateTime)fromDate) >= 0 &&
                DateTime.Compare(p.TradeDate, (DateTime)toDate) <= 0)
        }).ToListAsync();
    
        foreach (var client in clientswithpayments) {
            var totalsByCust = new SomeModel { Username = client.Username };
            foreach (var payment in client.payments) {
                totalByCust.Bcf += payment.Bcf;
                totalByCust.Ecn += payment.Ecn;
                totalByCust.Ecbt += payment.Ecbt;
                totalByCust.OpenGl += payment.OpenGl;
                totalByCust.JeyK += payment.JeyK;
    
            }
            totals.Add(totalByCust);
        }
        return totals;
    }
    

    在查询中处理这一切比较棘手,但可能是正确的答案:

    public async Task<List<SomeModel>> SomeMethod2(DateTime? fromDate, DateTime? toDate) {
        var totals = _db.Clients.Select(client => new {
            client.Username,
            payments = _db.Payments.Where(pay => pay.ClientId == client.Id).Where(p =>
                DateTime.Compare(p.TradeDate, (DateTime)fromDate) >= 0 &&
                DateTime.Compare(p.TradeDate, (DateTime)toDate) <= 0)
        }).Select(cwp => new SomeModel {
            Username = cwp.Username,
            Bcf = cwp.payments.Sum(),
            Ecn = cwp.payments.Ecn.Sum(),
            Ecbt = cwp.payments.Ecbt.Sum(),
            OpenGl = cwp.payments.OpenGl.Sum(),
            JeyK = cwp.payments.JeyK.Sum()
        }
    
        return await totals.ToListAsync();
    }
    

    【讨论】:

    • 它更快,但占用大量内存
    • 单查询方案怎么样?
    • 我已经尝试过这种方式,然后我得到了 systemOutOfMemory 异常 :) 这种方式需要超过 1 GB 的 RAM。无论如何,感谢您的关注。我真的很感激!
    • 使用单一查询解决方案?顺便说一句,我在刚刚修复的 sum 字段的单一查询解决方案 SomeModel 创建中有错字。
    • 是的,不允许
    【解决方案3】:

    确保您在 Payment to Client 上有一个导航属性。或者,您可以将所有客户端加载到字典中,因为它们只有 1500 个。这是使用导航属性的方式:

    public async Task<List<SomeModel>> SomeMethod(DateTime? fromDate, DateTime? toDate)
    {
      return _db.Payments
        .Where(p =>
            DateTime.Compare(p.TradeDate, (DateTime)fromDate) >= 0 &&
            DateTime.Compare(p.TradeDate, (DateTime)toDate) <= 0))
        .GroupBy(p=>p.ClientId)
        .Select(g=>new SomeModel { 
          UserName = g.First().Client.UserName,
          Bcf = g.Sum(p=>p.Bcf),
          Ecn = g.Sum(p=>p.Ecn),
          Ecbt = g.Sum(p=>p.Ecbt),
          OpenGl = g.Sum(p=>p.OpenGl),
          JeyK = g.Sum(p=>p.JeyK)
        })
        .ToListAsync();
    }
    

    【讨论】:

    • 这样做的缺点是,如果您的客户在该日期范围内没有付款,他们将不会出现。
    • 你可能也应该调用这个方法 SomeMethodAsync 因为它是一个真正的异步方法。
    • 您说得对,先生。我会重命名它......是的,它是一个真正的异步方法,可以在一行中接收大量查询
    【解决方案4】:

    使用 SQLCommand 将此代码移至传统 SQL 语句。 Linq 查询是逐行工作的,使用批处理查询效率更高。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-27
      • 2022-10-02
      • 2014-08-10
      • 1970-01-01
      • 1970-01-01
      • 2021-11-27
      • 1970-01-01
      • 2013-09-25
      相关资源
      最近更新 更多