Linq to Entity Query执行时间超过2分钟

时间:2016-01-19 11:56:43

标签: c# entity-framework linq oracle11g

我正在尝试使用LINQ to Entity执行以下查询,并且存在性能问题。返回结果大约需要2-3分钟。我需要有关如何提高这些查询性能的建议

这是我用来带来某些日期之间的订单总数的方法。这里的数据在两个表之间分开。我需要从一个表中获取交易编号,然后使用这些交易,我正在提取订单并将它们相加,但查询对于某些标准来说太慢了,我在第一个查询中返回了大约10k +记录。

r

1 个答案:

答案 0 :(得分:1)

此代码中至少存在两个问题

List<int> transactions = _context.RP_PART_TRANSACTIONS.Where(trd => TransactionNumbers.Contains(trd.TRANSACTION_NUMBER) && trd.TRANSACTION_DATE >= FromDate && trd.TRANSACTION_DATE <= ToDate).Select(tr => tr.TRANSACTION_NUMBER).ToList();

return _context.RP_PART_TRANSACTION_DETAILS.Where(trd => transactions.Contains(trd.TRANSACTION_NUMBER)).Select(tr => tr.PART_QTY).ToList().Sum();

首先,您使用两个查询,这些查询可以使用join运算符组合在一起。考虑到第一个查询可以返回大量记录(如你所提到的10K +),并且在数据库端无法有效处理有效在查询中使用内存Contains给你一个很大的改进。

其次,在后面的查询中,首先使用ToList(),然后使用Sum,这需要从数据库中读取所有记录并将它们在内存中求和。让数据库做总和是非常有效的。

说到这一切,值得尝试以下代替

var result =
    (from td in _context.RP_PART_TRANSACTION_DETAILS
     join t in _context.RP_PART_TRANSACTIONS
         on td.TRANSACTION_NUMBER equals t.TRANSACTION_NUMBER
     where TransactionNumbers.Contains(t.TRANSACTION_NUMBER)
         && t.TRANSACTION_DATE >= FromDate && t.TRANSACTION_DATE <= ToDate
     select td.PART_QTY)
    .Sum();

更新:从您的TransactionNumbers包含~16K项目的评论中注意到。 EF 会将TransactionNumbers.Contains(t.TRANSACTION_NUMBER)部分转换为SQL t.TRANSACTION_NUMBER IN (...)子句,并在IN子句中列出16K数字,这将导致Oracle CBO选择全表扫描而不是索引扫描。您可以通过包含列表的下限和上限来尝试强制索引范围扫描,如此

var minNumber = TransactionNumbers.Min();
var maxNumber = TransactionNumbers.Max();
var result =
    (from td in _context.RP_PART_TRANSACTION_DETAILS
     join t in _context.RP_PART_TRANSACTIONS
         on td.TRANSACTION_NUMBER equals t.TRANSACTION_NUMBER
     where t.TRANSACTION_NUMBER >= minNumber && t.TRANSACTION_NUMBER <= maxNumber
         && TransactionNumbers.Contains(t.TRANSACTION_NUMBER)
         && t.TRANSACTION_DATE >= FromDate && t.TRANSACTION_DATE <= ToDate
     select td.PART_QTY)
    .Sum();

如果仍然很慢,我能想到的最后一件事就是尝试尽可能多地在数据库中进行过滤/聚合(无TransactionNumbers过滤器),然后在内存中进行最终过滤/聚合,像这样

var query =
    from td in _context.RP_PART_TRANSACTION_DETAILS
    join t in _context.RP_PART_TRANSACTIONS
        on td.TRANSACTION_NUMBER equals t.TRANSACTION_NUMBER
    where t.TRANSACTION_DATE >= FromDate && t.TRANSACTION_DATE <= ToDate
    group td by t.TRANSACTION_NUMBER into g
    select new { TRANSACTION_NUMBER = g.Key, PART_QTY = g.Sum(td => td.PART_QTY) };

var filter = new HashSet<int>(TransactionNumbers); // For efficient lookup

var result = query.AsEnumerable() // Important! Switch to in memory context
    .Where(td => filter.Contains(td.TRANSACTION_NUMBER))
    .Sum(td => td.PART_QTY);