要优化我的linq查询,该查询在azure查询性能洞察工具中列为最常用的查询

时间:2017-07-29 09:56:23

标签: c# entity-framework linq azure azure-sql-database

我的Linq查询要优化:

var onlineData = (from od in peopleStatus.AsNoTracking().ToList()

let location = (from zone in db.RTLS_ZONEDTLS
                where zone.zone_id == od.Person.Zone_ID
                select zone.area).AsNoTracking().FirstOrDefault()

let zoneIdsArray = getZoneList((od.ZoneIds.ToArray()))
let fzones = zoneIdsArray.Select(z => z).Take(4)

select new OnlineDataInfoDTO
{
    P_ID = od.Person.PERSONID,
    T_ID = (int)od.Person.TAGID,
    Z_ID = (od.Person.created_time >= startOfThisDay) ? (int)od.Person.Zone_ID : -1,
    LOC = (location != null ? location : " "),
    STATUS = (od.Person.created_time >= startOfThisDay) ? (int)od.Person.status : 6,
    T_BAT_SIG_STR = (int)od.Person.TAG_SIGNALSTRENGTH,
    B_BAT_SIG_STR = (int)od.Person.BS_SIGNALSTRENGTH,
    T_BAT_STA_VAL = (int)od.Person.TAG_BATTERY_STATUS_VAL,
    T_BAT_STA_PERCNT = (int)od.Person.TAG_BATTERY_STATUS_PERCNT,
    BS_BAT_STA_VAL = (int)od.Person.BS_BATTERY_STATUS_VAL,
    BS_BAT_STA_PER = (int)od.Person.BS_BATTERY_STATUS_PERCNT,
    IN_TIME = (od.Person.INTIME).ToString(),
    ALL_NOT_TME = (od.Person.ALLISNOTWELLTIME).ToString(),
    P_TME = (od.Person.PANICTIME).ToString(),
    NO_M_TME = (od.Person.NOMOTIONTIME).ToString(),
    OUT_TME = (od.Person.OUT_TIME).ToString(),
    TEMP_TME = (od.Person.TEMPEXCEEDTIME).ToString(),
    LOW_BAT_TME = (od.Person.LOW_BATTERY_TIME).ToString(),
    FOUT_TME = (od.Person.FOUT_TIME).ToString(),
    LAST_UPDATE_TIME = (od.Person.LASTUPDATEDTIME).ToString(),
    TEMP_VAL = (decimal)(od.Person.TEMP_VALUE),


    NO_OF_OUT = (
      from o in db.RTLS_FAULT_DTLS
      where (o.faultno == (int)Constants.Faults.LowBattery)
      where (startOfThisDay <= o.ORC_DATETIME && o.ORC_DATETIME <= todayEndDate)
      where (o.PERSON_ID.ToLower() == od.Person.PERSONID.ToLower())
      select o.fltname).Count(),
    NO_OF_PANIC = (
      from o in db.RTLS_FAULT_DTLS
      where o.faultno == (int)Constants.Faults.Panic
      where (startOfThisDay <= o.ORC_DATETIME && o.ORC_DATETIME <= todayEndDate)
      where (o.PERSON_ID.ToLower() == od.Person.PERSONID.ToLower())
      select o.fltname).Count(),
    NO_OF_IN_ACTIVE = (
      from o in db.RTLS_FAULT_DTLS
      where o.faultno == (int)Constants.Faults.InActive
      where (startOfThisDay <= o.ORC_DATETIME && o.ORC_DATETIME <= todayEndDate)
      where (o.PERSON_ID == od.Person.PERSONID)
      select o.fltname).Count(),
    NO_OF_TEMP = (
      from o in db.RTLS_FAULT_DTLS
      where (o.faultno == (int)Constants.Faults.HighTemp || o.faultno == (int)Constants.Faults.LowTemp)
      where (startOfThisDay <= o.ORC_DATETIME && o.ORC_DATETIME <= todayEndDate)
      where (o.PERSON_ID == od.Person.PERSONID)
      select o.fltname).Count(),
    NO_OF_LBAT = (
      from o in db.RTLS_FAULT_DTLS
      where o.faultno == (int)Constants.Faults.LowBattery
      where (startOfThisDay <= o.ORC_DATETIME && o.ORC_DATETIME <= todayEndDate)
      where (o.PERSON_ID == od.Person.PERSONID)
      select o.fltname).Count(),
    LOCS = fzones.ToList()
}).ToList();

我的getZoneList方法如下所示,在上面的查询中使用。

public int[] getZoneList(decimal[] zoneIdsArray)
{
    int[] zoneIds = Array.ConvertAll(zoneIdsArray, x => (int)x);
    List<int> list = zoneIds.ToList();
    for (int c = 1; c < zoneIdsArray.Count(); c++)
    {
        if (zoneIdsArray[c] == zoneIdsArray[c - 1])
        {
            list.Remove((int)zoneIdsArray[c]);
        }
    }
    return list.ToArray();
}

对于我的应用程序,我在visual studio-2015中使用WebApi2编写了一个Web服务,其中Entity框架为ORM,Azure_SQL为后端。我观察到上面的linq查询至少需要25秒到最多1.5分钟才能执行(使用azure portal的查询性能洞察工具来查找它)。 在逐行评论和测试之后,我逐渐了解了

from od in peopleStatus.AsNoTracking().ToList()

这条线花了很多时间(平均55秒)。我无法跳过转换为List以进一步使用。建议我如何优化查询以减少整体周转时间。

2 个答案:

答案 0 :(得分:1)

如果我没记错的话,这段代码

 NO_OF_OUT = (from o in db.RTLS_FAULT_DTLS
                                                       where (o.faultno == (int)Constants.Faults.LowBattery)
                                                       where (startOfThisDay <= o.ORC_DATETIME && o.ORC_DATETIME <= todayEndDate)
                                                       where (o.PERSON_ID.ToLower() == od.Person.PERSONID.ToLower())
                                                       select o.fltname).Count(),
                                          NO_OF_PANIC = (from o in db.RTLS_FAULT_DTLS
                                                         where o.faultno == (int)Constants.Faults.Panic
                                                         where (startOfThisDay <= o.ORC_DATETIME && o.ORC_DATETIME <= todayEndDate)
                                                         where (o.PERSON_ID.ToLower() == od.Person.PERSONID.ToLower())
                                                         select o.fltname).Count(),
                                          NO_OF_IN_ACTIVE = (from o in db.RTLS_FAULT_DTLS
                                                             where o.faultno == (int)Constants.Faults.InActive
                                                             where (startOfThisDay <= o.ORC_DATETIME && o.ORC_DATETIME <= todayEndDate)
                                                             where (o.PERSON_ID == od.Person.PERSONID)
                                                             select o.fltname).Count(),
                                          NO_OF_TEMP = (from o in db.RTLS_FAULT_DTLS
                                                        where (o.faultno == (int)Constants.Faults.HighTemp || o.faultno == (int)Constants.Faults.LowTemp)
                                                        where (startOfThisDay <= o.ORC_DATETIME && o.ORC_DATETIME <= todayEndDate)
                                                        where (o.PERSON_ID == od.Person.PERSONID)
                                                        select o.fltname).Count(),
                                          NO_OF_LBAT = (from o in db.RTLS_FAULT_DTLS
                                                        where o.faultno == (int)Constants.Faults.LowBattery
                                                        where (startOfThisDay <= o.ORC_DATETIME && o.ORC_DATETIME <= todayEndDate)
                                                        where (o.PERSON_ID == od.Person.PERSONID)
                                                        select o.fltname).Count(),

基本相同,唯一的区别是你想要计算的错误。您可以尝试使用group by faultno并立即获取所有内容。

编辑: Linq不是我的强项,但也许这会起作用:

from o in db.RTLS_FAULT_DTLS
            where (startOfThisDay <= o.ORC_DATETIME && o.ORC_DATETIME <= todayEndDate)
            where (o.PERSON_ID == od.Person.PERSONID)
            group o by o.faultno
            into g
            select new
            {
                faultno = g.Key,
                count = g.Count()
            };

答案 1 :(得分:0)

最后,通过删除 AsNoTracking()。ToList(),我将查询执行时间从55秒减少到平均4.5秒。

旧查询花了太多时间执行。

  var onlineData = from od in peopleStatuc.AsNoTracking().ToList()

我的总周转时间大幅下降的新查询。

  var onlineData = from od in peopleStats

我发现在执行子查询时,我不必要地将我的结果转换为列表(在记录增加时将结果转换为列表的时间增加),因此将其删除。