运行IQueryable.First()时的性能问题

时间:2017-07-18 12:23:59

标签: c# performance linq

我正在为一所抓住活跃学生的大学制作一个计划,然后为其他流程提供报告。

其中一项重要功能是查看学生是否已毕业。

  • 如果学生毕业并且没有回来,他们就不会被视为活跃的学生。
  • 如果他们毕业并且正在回来或没有毕业并且又回来了另一个学期,他们被认为是活跃的。

当学生通过主要功能时,通过该过程大约需要5秒钟。我发现参与该过程的最多时间来自此函数中的IQueryable.First()

public static bool ContinuingEducation(string v)
{
    var TERMSSTU = from t in _DB.TERMs
                        join stu in _DB.STUDENT_TERMS_VIEWs
                        on t.TERMS_ID equals stu.STTR_TERM
                        where v == stu.STTR_STUDENT
                        orderby t.TERM_START_DATE descending
                        select new { startdate = t.TERM_START_DATE };
    var graduation = from a in _DB.ACAD_CREDENTIALs
                         where v == a.ACAD_PERSON_ID
                         orderby a.ACAD_COMMENCEMENT_DATE ascending
                         select a;

    if (graduation.Count() > 0 && TERMSSTU.Count() > 0)
    {
        if (TERMSSTU.First().startdate > graduation.First().ACAD_COMMENCEMENT_DATE) // the problem is here
            return true;
    }
    return false;
}

我不知道为什么这么久需要这么久。是否有更好的方法来写出这个函数,因为它更快?

4 个答案:

答案 0 :(得分:4)

您对Count的使用效率很低,因为您需要多次查询数据库(一次获取Count,一次获取First)。以下代码更改将无需获取Count

变化:

if (graduation.Count() > 0 && TERMSSTU.Count() > 0)
{
    if (TERMSSTU.First().startdate > graduation.First().ACAD_COMMENCEMENT_DATE) // the problem is here
        return true;
}
return false;

为:

var grad = graduation.FirstOrDefault();
var term = TERMSSTU.FirstOrDefault()

if (grad == null || term == null)
    return false;

return term.startdate > grad.ACAD_COMMENCEMENT_DATE;

请注意,即使进行此更改,FirstOrDefault仍然可能会很慢。要解决此问题,您应该运行SQL Trace以查看正在生成的SQL。然后查看add indexes /优化查询。

答案 1 :(得分:2)

尝试代码

public static bool ContinuingEducation(string v)
{
     var TERMSSTU = (from t in _DB.TERMs.AsQueryable()
                    join stu in _DB.STUDENT_TERMS_VIEWs.AsQueryable()
                    on t.TERMS_ID equals stu.STTR_TERM
                    where v == stu.STTR_STUDENT
                    orderby t.TERM_START_DATE descending
                    select new { startdate = t.TERM_START_DATE }).FirstOrDefault();
     var graduation = (from a in _DB.ACAD_CREDENTIALs.AsQueryable()
                     where v == a.ACAD_PERSON_ID
                     orderby a.ACAD_COMMENCEMENT_DATE ascending
                     select a).FirstOrDefault();

    if (graduation==null  || TERMSSTU==null)
         return false;

     return TERMSSTU.startdate >graduation.ACAD_COMMENCEMENT_DATE;
}

答案 2 :(得分:0)

性能问题不在First调用中,而在查询中。当您致电First时会对其进行评估。

您应该重写代码以使用较少的查询:eq您可以在v参数上加入两个查询,然后获取结果并检查它的日期。它将导致单个查询。

答案 3 :(得分:0)

我最好的办法是获得一些严肃的效率,就是在程序开始时查询这些记录,这样所有记录都在内存中,这使得程序的查询变得轻而易举。

  1. 我创建了一个捕获这两个查询的新类

    公共类TERMSTU {   公共日期时间? startdate {get;组; }   public String Id {get;组; } }

    公共课毕业     {         公共日期时间? ACAD_COMMENCEMENT_DATE {get;组; }         public String Id {get;组; }     } 公共课报告 {     私人清单TERMSSTUS;     私人名单毕业;     公共报告(ColleagueDataContext _DB)     {         var TERMSSTU =来自_DB.TERMs中的t                        在_DB.STUDENT_TERMS_VIEWs中加入stu                        在t.TERMS_ID等于stu.STTR_TERM                        orderby t.TERM_START_DATE降序                        选择新{startdate = t.TERM_START_DATE,id = stu.STTR_STUDENT};         TERMSSTUS =新名单();         foreach(TER IS中的var i)         {             TERMSSTUS.Add(new TERMSTU(){Id = i.id,startdate = i.startdate});         }

        var graduation = from a in _DB.ACAD_CREDENTIALs
                         where a.ACAD_COMMENCEMENT_DATE != null
                         where a.ACAD_COMMENCEMENT_DATE < DateTime.Today
                         orderby a.ACAD_COMMENCEMENT_DATE descending
                         select a;
    
        GRADUATIONS = new List<Graduation>();
        foreach (var i in graduation)
        {
            GRADUATIONS.Add(new Graduation() { Id = i.ACAD_PERSON_ID, ACAD_COMMENCEMENT_DATE = i.ACAD_COMMENCEMENT_DATE });
        }
    }
    public List<TERMSTU> givestu()
    {
        return TERMSSTUS;
    }
    public List<Graduation> givegrad()
    {
        return GRADUATIONS;
    }
    

    }

  2. 我修改了该功能以进行两次查询

    public static bool ContinuingEducation(string v, List<TERMSTU> t, List<Graduation> g)
    {
        var term = (from stu in t where stu.Id == v select stu).FirstOrDefault();
        var grad = (from gradu in g where gradu.Id == v select gradu).FirstOrDefault();
        return term.startdate > grad.ACAD_COMMENCEMENT_DATE.Value.AddDays(-14);
    }
    
  3. 这种方法有更多的代码可以编写,但是当程序必须经过3000条左右的记录时,它会节省大量的时间。