如何优化在500,000表上花费11分钟的EntityFramework查询

时间:2016-06-10 05:21:49

标签: c# sql-server entity-framework linq

我有一个包含500,000条记录的表,并且在我的应用程序中我制作了一些过滤器,查询返回给我410,000,现在不要误会我的意思,我不会在屏幕上显示那些数据,它每天都是一个财务应用程序表需要处理并在内存中进行一些计算,并将一些总计写回其他表。

我不想粘贴我的整个模型,因为它不符合这个问题的目的,问题是如何优化EF以使查询更快,我尝试使用SQL Profile并返回410,000需要11分钟。

private List<MasterVenta> FilterMasterVentas(RuleEditor filter, ComisionPorProveedor comisionPorProveedor)
        {
            Periodo periodo = comisionPorProveedor.Periodo;
            var strReferences = new StringBuilder();
            var Modelo_PuntoVentaProveedor = nameof(MasterVenta.PuntoVentaProveedor);
            var Modelo_PuntoVentaProveedor_proveedor = Modelo_PuntoVentaProveedor + "." + nameof(PuntosVentaProveedor.Proveedor);
            var Modelo_PuntoVentaProveedor_Liquidaciones = Modelo_PuntoVentaProveedor + "." + nameof(PuntosVentaProveedor.Liquidaciones);
            var Modelo_PuntoVentaProveedor_Proveedor_TipoDeCanal = Modelo_PuntoVentaProveedor_proveedor + "." + nameof(Proveedor.TipoDeCanal);
            //var Modelo_PuntoVentaProveedor_Proveedor_Productos = Modelo_PuntoVentaProveedor_proveedor + "." + nameof(Proveedor.Productos);
            var Modelo_PuntoVentaProveedor_Proveedor_ContactosProveedor = Modelo_PuntoVentaProveedor_proveedor + "." + nameof(Proveedor.ContactosProveedor);
            var Modelo_PuntoVentaProveedor_Proveedor_Contrato = Modelo_PuntoVentaProveedor_proveedor + "." + nameof(Proveedor.Contrato);
            var Modelo_PuntoVentaProveedor_Proveedor_ComisionesPorProveedor = Modelo_PuntoVentaProveedor_proveedor + "." + nameof(Proveedor.ComisionesPorProveedor);


            strReferences.Append(Modelo_PuntoVentaProveedor);
            strReferences.Append("," + Modelo_PuntoVentaProveedor_Liquidaciones);
            strReferences.Append("," + Modelo_PuntoVentaProveedor_Proveedor_TipoDeCanal);
            //strReferences.Append("," + Modelo_PuntoVentaProveedor_Proveedor_Productos);
            strReferences.Append("," + Modelo_PuntoVentaProveedor_Proveedor_ContactosProveedor);
            strReferences.Append("," + Modelo_PuntoVentaProveedor_Proveedor_Contrato);
            strReferences.Append("," + Modelo_PuntoVentaProveedor_Proveedor_ComisionesPorProveedor);

            //el master de ventas debe filtrase por el periodo, PuntoVenta, TipoDeCanal,Producto

            List<int> puntosVenta = comisionPorProveedor.Proveedor.PuntosVentaProveedor.Select(p => p.PuntoVentaProveedorId).ToList();//tipo de canal de proveedor de acuerdo a la tabla comisionPorProveedor



            if (filter.Rule.IsEmpty()) return null;
            if (!filter.Rule.IsValid()) return null;
            var q = (from c in this.unitOfWork.MasterVentaRepository.Get(null, o => o.OrderBy(x => x.MasterVentaID), strReferences.ToString()).Filter<MasterVenta>(filter.Rule.GetRuleXml())
                     where c.FechaVenta != null &&
                     (
                      (DateTime.Compare(c.FechaVenta.Value, periodo.FechaFinal) <= 0)
                      && (DateTime.Compare(c.FechaVenta.Value, periodo.FechaInicial) >= 0)
                     )
                     &&
                     (c.PuntoVentaProveedor != null &&
                     (
                     (puntosVenta.Any(pv => c.PuntoVentaProveedor != null && pv == c.PuntoVentaProveedor.PuntoVentaProveedorId)
                     || c.PuntoVentaProveedor.PuntoDeVentaHistorials.Any(p => c.PuntoVentaProveedor != null && c.FechaVenta >= p.FechaInicio && c.FechaVenta <= p.FechaFin))


                     ))

                     select c);
            var result = q.ToList();
            return result;


        }

需要11分钟的这一行是

var q = (from c in this.unitOfWork.MasterVentaRepository.Get(null, o => o.OrderBy(x => x.MasterVentaID), strReferences.ToString()).Filter<MasterVenta>(filter.Rule.GetRuleXml())
                     where c.FechaVenta != null &&
                     (
                      (DateTime.Compare(c.FechaVenta.Value, periodo.FechaFinal) <= 0)
                      && (DateTime.Compare(c.FechaVenta.Value, periodo.FechaInicial) >= 0)
                     )
                     &&
                     (c.PuntoVentaProveedor != null &&
                     (
                     (puntosVenta.Any(pv => c.PuntoVentaProveedor != null && pv == c.PuntoVentaProveedor.PuntoVentaProveedorId)
                     || c.PuntoVentaProveedor.PuntoDeVentaHistorials.Any(p => c.PuntoVentaProveedor != null && c.FechaVenta >= p.FechaInicio && c.FechaVenta <= p.FechaFin))


                     ))

.get方法就像这样:

public virtual IEnumerable<TEntity> Get(
        Expression<Func<TEntity, bool>> filter = null,
        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
        string includeProperties = "")
    {
      IQueryable<TEntity> query = dbSet.AsNoTracking();

      if (filter != null)
      {
        query = query.Where(filter);
      }

      foreach (var includeProperty in includeProperties.Split
          (new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
      {
        query = query.Include(includeProperty);
      }

4 个答案:

答案 0 :(得分:2)

好的,提及您在其中一条评论中使用Code Efects对您的问题至关重要。

查看他们的网页,似乎他们能够integrate with Entity Framework to produce proper Where clause。你只是错误地使用它。

要正常使用,需要在awk '{print int($0/4)*4}' 而不是IQueryable上运行。如果你摆脱了IEnumerable方法,而是直接在存储库上运行它,它应该开始生成正确的SQL数据库查询,而不是在内存中进行过滤。

他们的文件相关引用(强调我的):

  

这是使用代码效果&#39;的一个简单示例。使用LINQ to Object的Get扩展方法,但Code Effects&#39;的真正优势通过将过滤器应用于实体框架查询来演示基于规则的数据过滤。 LINQ to Entity提供程序的工作是将所有表达式转换为SQL语句并在数据库服务器上执行它们。由于Filter()扩展名包含正确的表达式,因此提供程序会将过滤器转换为正确的SQL Filter子句,在服务器上运行生成的语句,并返回已过滤的结果设置而不是先检索所有数据,然后在内存中过滤结果集

答案 1 :(得分:1)

通常,延迟是由网络延迟和传输该数据量所需的时间引起的。您希望最大限度地减少传输的数据量,因此需要采用以下几种方法:

  1. 创建一个映射到表的轻量级对象,只返回您绝对需要的表中的列。
  2. 您可以汇总SQL Server上的任何数据并返回汇总数据以进行计算吗?

答案 2 :(得分:1)

Entity Framework提供了许多性能调优选项,可帮助您优化应用程序的性能。其中一个调整选项是.AsNoTracking()。此优化允许您告知实体框架不跟踪查询的结果。这意味着实体框架不会执行查询返回的实体的其他处理或存储。但是,这也意味着您无法更新这些实体,而无需将它们重新连接到跟踪图。

来源HERE

这应该会大大提高性能。 你可以从生成的SQL中检查一些调整。但是,总会有一个点,你必须去数据库进行优化,如分片,索引等

答案 3 :(得分:1)

问题是这不是LinqToEntityFramework查询。这是一个LinqToObject查询。

你的linq to EntityFramework查询就在这里。

List<int> puntosVenta = comisionPorProveedor.Proveedor.PuntosVentaProveedor.Select(p => p.PuntoVentaProveedorId).ToList();//tipo de canal de proveedor de acuerdo a la tabla comisionPorProveedor

this.unitOfWork.MasterVentaRepository.Get(null, o => o.OrderBy(x => x.MasterVentaID), strReferences.ToString())

在这两种情况下,您都将整个表格的数据提取到.net。

您无法优化查询,因为瓶颈将是BANDWIDTH。

这是一个常见的错误。您认为仅仅因为您的代码位于Linq查询中,它就是LinqToEntityFramework查询。这是错误的。

  • 您需要在这里完全从头开始。您的查询中根本不应该有任何方法调用(INLINE EVERYTHING)。

  • 您不能使用DateTime.Compare等任何系统方法。用date1 > date2不等式替换那些(我们这里不是JAVA开发人员)。

  • 请记住,一切顺利。 EF需要能够将其转换为SQL查询。因此,查询中不应包含自定义.net类型(POCO除外)。