简体   繁体   中英

How to optimize LINQ DB request?

I have next code, taking Last and Pre-Last params of elemensts in DB:

var result =  _context.Contacts.Where(conts => conts.CreatorUserId == _userManager.GetUserId(this.User))
     .Select(conts => new
     {
         conts.ID,
         conts.Mobile,
         conts.Email,
         conts.Facebook,
         conts.StateId,
         conts.CreatorUserId,
         conts.Birthday,
         conts.Description,
         conts.Name,
         conts.Photo,
         conts.SecondName,

         Tags = conts.Tags.Select(d=> d.UserTag.Emoji),
         NpaId = conts.NpaInfo.NpaId,

         PartnerPvPrev = conts.NpaInfo.NpaData.OrderByDescending(x => x.DataMonth).Skip(1).Take(1).Select(x => x.MyPV).FirstOrDefault(),
         GroupPvPrev = conts.NpaInfo.NpaData.OrderByDescending(x => x.DataMonth).Skip(1).Take(1).Select(x => x.GroupPV).FirstOrDefault(),
         LevelPrev = conts.NpaInfo.NpaData.OrderByDescending(x => x.DataMonth).Skip(1).Take(1).Select(x => x.PerformanceBonusLevelId).FirstOrDefault(),


         PartnerPv = conts.NpaInfo.NpaData.OrderByDescending(x => x.DataMonth).Take(1).Select(x => x.MyPV).FirstOrDefault(),
         Level = conts.NpaInfo.NpaData.OrderByDescending(x => x.DataMonth).Take(1).Select(x => x.PerformanceBonusLevelId).FirstOrDefault(),// conts.NpaInfo.PerformanceBonusLevelId,
         GroupPv = conts.NpaInfo.NpaData.OrderByDescending(x => x.DataMonth).Take(1).Select(x => x.GroupPV).FirstOrDefault(),

         EntryDate = conts.NpaInfo.EntryDate,
         ExpirationDate = conts.NpaInfo.ExpirationDate
     });

In fact the part :

PartnerPvPrev = conts.NpaInfo.NpaData.OrderByDescending(x => x.DataMonth).Skip(1).Take(1).Select(x => x.MyPV).FirstOrDefault(),
GroupPvPrev = conts.NpaInfo.NpaData.OrderByDescending(x => x.DataMonth).Skip(1).Take(1).Select(x => x.GroupPV).FirstOrDefault(),
LevelPrev = conts.NpaInfo.NpaData.OrderByDescending(x => x.DataMonth).Skip(1).Take(1).Select(x => x.PerformanceBonusLevelId).FirstOrDefault()

converts to something like this in 1 request (show you only part for one block):

 SELECT TOP(1) [t0].[MyPV]
    FROM (
        SELECT [x0].[MyPV], [x0].[DataMonth]
        FROM [NpaDatas] AS [x0]
        WHERE [conts.NpaInfo].[ID] = [x0].[NpaInfoId]
        ORDER BY [x0].[DataMonth] DESC
        OFFSET 1 ROWS FETCH NEXT 1 ROWS ONLY
    ) AS [t0]
    ORDER BY [t0].[DataMonth] DESC
) AS [PartnerPvPrev], (
    SELECT TOP(1) [t1].[GroupPV]
    FROM (
        SELECT [x1].[GroupPV], [x1].[DataMonth]
        FROM [NpaDatas] AS [x1]
        WHERE [conts.NpaInfo].[ID] = [x1].[NpaInfoId]
        ORDER BY [x1].[DataMonth] DESC
        OFFSET 1 ROWS FETCH NEXT 1 ROWS ONLY
    ) AS [t1]
    ORDER BY [t1].[DataMonth] DESC
) AS [GroupPvPrev], (
    SELECT TOP(1) [t2].[PerformanceBonusLevelId]
    FROM (
        SELECT [x2].[PerformanceBonusLevelId], [x2].[DataMonth]
        FROM [NpaDatas] AS [x2]
        WHERE [conts.NpaInfo].[ID] = [x2].[NpaInfoId]
        ORDER BY [x2].[DataMonth] DESC
        OFFSET 1 ROWS FETCH NEXT 1 ROWS ONLY
    ) AS [t2]
    ORDER BY [t2].[DataMonth] DESC
) AS [LevelPrev]

I dont want to repeat the same part multiple times:

conts.NpaInfo.NpaData.OrderByDescending(x => x.DataMonth).Skip(1).Take(1)

So, if I do like this :

PrevBuffer = conts.NpaInfo.NpaData.OrderByDescending(x => x.DataMonth).Skip(1).Take(1).Select(                               
     param => new
     {

         param.MyPV,
         param.GroupPV,
         param.PerformanceBonusLevelId
     }
     ),

It will display many separate requests in output for each row like:

SELECT [x0].[MyPV], [x0].[GroupPV], [x0].[PerformanceBonusLevelId]
FROM [NpaDatas] AS [x0]
WHERE @_outer_ID1 = [x0].[NpaInfoId]
ORDER BY [x0].[DataMonth] DESC
OFFSET 1 ROWS FETCH NEXT 1 ROWS ONLY

The same is about this part :

Tags = conts.Tags.Select(d=> d.UserTag.Emoji)

Will get multiple selects like:

SELECT [d.UserTag].[Emoji]
FROM [ContactTags] AS [d]
LEFT JOIN [UserTags] AS [d.UserTag] ON [d].[UserTagId] = [d.UserTag].[ID]
WHERE @_outer_ID = [d].[ContactId]

Can it be optimized somehow?

Doing something like this will improve the query significantly.

var result = conts.NpaInfo.NpaData.OrderByDescending(x => x.DataMonth).Take(2).ToArray();

PartnerPvPrev = result.Skip(1).Take(1).Select(x => x.MyPV).FirstOrDefault();
  GroupPvPrev = result.Skip(1).Take(1).Select(x => x.GroupPV).FirstOrDefault();
    LevelPrev = result.Skip(1).Take(1).Select(x => x.PerformanceBonusLevelId).FirstOrDefault();
    PartnerPv = result.Take(1).Select(x => x.MyPV).FirstOrDefault();
        Level = result.Take(1).Select(x => x.PerformanceBonusLevelId).FirstOrDefault();
      GroupPv = result.Take(1).Select(x => x.GroupPV).FirstOrDefault();

You should select the repetitive parts first and use these in the subsequent query. This is much easier in query syntax, using the let keyword:

var result = from conts in _context.Contacts
    where conts.CreatorUserId == _userManager.GetUserId(this.User)
    let monthsSorted = conts.NpaInfo.NpaData.OrderByDescending(x => x.DataMonth)
    let firstMonth = monthsSorted.FirstOrDefault()
    let prevMonth = monthsSorted.Skip(1).FirstOrDefault()
    select new
    {
        conts.ID,
        conts.Mobile,
        conts.Email,
        conts.Facebook,
        conts.StateId,
        conts.CreatorUserId,
        conts.Birthday,
        conts.Description,
        conts.Name,
        conts.Photo,
        conts.SecondName,

        Tags = conts.Tags.Select(d=> d.UserTag.Emoji),
        NpaId = conts.NpaInfo.NpaId,

        PartnerPvPrev = prevMonth.MyPV,
        GroupPvPrev = prevMonth.GroupPV,
        LevelPrev = prevMonth.PerformanceBonusLevelId,

        PartnerPv = firstMonth.MyPV,
        GroupPv = firstMonth.GroupPV,
        Level = firstMonth.PerformanceBonusLevelId,

        EntryDate = conts.NpaInfo.EntryDate,
        ExpirationDate = conts.NpaInfo.ExpirationDate
    };

You should use a GroupBy :

    class Program
    {
        static void Main(string[] args)
        {
            Conts conts = new Conts();

            List<NpaDatas> data = conts.NpaInfo.NpaDatas
                .OrderByDescending(x => x.DataMonth)
                .GroupBy(x => x.DataMonth).Select(x => x.FirstOrDefault()).ToList();

            //To get the second latest use followng
            NPaDatas results = data.Skip(1).FirstOrDefault();
        }
    }

    public class NpaDatas
    {
        public string NpaInfoId { get; set; }
        public DateTime DataMonth { get; set; }
        public PV PartnerPv { get; set; }
        public PV PerformanceBonusLevelId { get; set; }
        public PV GroupPv { get; set; }
    }
    public class PV
    {
        //data not specified 
    }
    public class Conts
    {
        public NpaInfo NpaInfo { get; set; }
    }
    public class NpaInfo
    {
        public string ID { get; set; }
        public List<NpaDatas> NpaDatas { get; set; }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM