EFCore2.2中使用Group By的那些坑及解決方法


背景

  在后端使用EFCore進行數據庫操作的時候,不可避免的要進行Group By操作,在進行Group By后有時候進行Sum的操作的時候EFCore是不能按照我們的預期生成正確的SQL的,而且這個問題EFCore的官方也沒有給出一個好的解決方式,那么在使用EFCore2.2進行開發的時候這些問題又不可避免,那么我們該如何規避這個問題,並且使之生成我們期望的SQL語句呢,本篇我們我們就這個出現這個問題的現象以及解決方案來進行分析,來看看在現階段到底該如何解決這個問題。

正常情況

  我們先來看一下能夠正常生成SQL的EFCore的寫法,這種情況下會生成符合預期的SQL語句並且沒有什么問題,我們先來看看正常情況的EFCore的代碼。

        /// <summary>
        /// 查詢月度輔導任務清單列表
        /// </summary>
        /// <param name="input">查詢輸入</param>
        /// <param name="pageRequest">分頁請求</param>
        /// <returns>帶分頁的月度任務清單</returns>
        public async Task<Page<GetCoachTaskDetailOutput>> GetCoachTaskDetailExAsync(GetCoachTaskDetailInput input, PageRequest pageRequest) {
            var queryResults = from v in from coachTask in _coachTaskRepository.GetAll()
                                      .WhereIf(!string.IsNullOrWhiteSpace(input.Code), c => c.Code.Contains(input.Code))
                                      .WhereIf(input.Year.HasValue, c => c.Year == input.Year)
                                      .WhereIf(input.Quarter.HasValue, c => c.Quarter == input.Quarter)
                                      .WhereIf(input.Month.HasValue, c => c.Month == input.Month)
                                      .WhereIf(!string.IsNullOrWhiteSpace(input.MarketingResponsibleName),
                                          c => c.MarketingResponsibleName.Contains(input.MarketingResponsibleName))
                                      .WhereIf(input.MarketingDepartmentIds.Any(), c => input.MarketingDepartmentIds.Contains(c.MarketingDepartmentId))
                                         join coachTaskDetail in _coachTaskDetailRepository.GetAll()
                                                 .WhereIf(!string.IsNullOrWhiteSpace(input.DealerCode), d => d.DealerCode.Contains(input.DealerCode))
                                                 .WhereIf(input.Status.Any(), d => input.Status.Contains(d.Status))
                                                 .WhereIf(input.ReviewStatus.HasValue, d => d.ReviewStatus == input.ReviewStatus)
                                                 .WhereIf(input.BeginActualCoachTime.HasValue, d => input.BeginActualCoachTime <= d.ActualCoachTime)
                                                 .WhereIf(input.EndActualCoachTime.HasValue, d => d.ActualCoachTime <= input.EndActualCoachTime)
                                             on coachTask.Id equals coachTaskDetail.CoachTaskId
                                         join coachTaskItemDetail in _coachTaskItemDetailRepository.GetAll()
                                                 .WhereIf(input.Type.HasValue, i => i.Type == input.Type)
                                             on coachTaskDetail.Id equals coachTaskItemDetail.CoachTaskDetailId
                                         select new {
                                             coachTaskDetail.Id,
                                             coachTask.Code,
                                             coachTask.Year,
                                             coachTask.Quarter,
                                             coachTask.Month,
                                             coachTaskDetail.DealerCode,
                                             coachTaskDetail.DealerName,
                                             coachTaskDetail.Status,
                                             coachTaskDetail.ReviewStatus,
                                             coachTask.MarketingDepartmentName,
                                             coachTask.MarketingResponsibleName,
                                             coachTaskDetail.ActualerCoachName,
                                             coachTaskDetail.ActualCoachTime,
                                             coachTaskItemDetail.StandardScore,
                                             coachTaskItemDetail.IsQualified
                                         }
                               group v by new {
                                   v.Id,
                                   v.Code,
                                   v.Year,
                                   v.Quarter,
                                   v.Month,
                                   v.DealerCode,
                                   v.DealerName,
                                   v.Status,
                                   v.ReviewStatus,
                                   v.MarketingDepartmentName,
                                   v.MarketingResponsibleName,
                                   v.ActualerCoachName,
                                   v.ActualCoachTime,
                               } into tempGroups
                               select new GetCoachTaskDetailGroupByResultModel {
                                   Id = tempGroups.Key.Id,
                                   Code = tempGroups.Key.Code,
                                   Year = tempGroups.Key.Year,
                                   Quarter = tempGroups.Key.Quarter,
                                   Month = tempGroups.Key.Month,
                                   DealerCode = tempGroups.Key.DealerCode,
                                   DealerName = tempGroups.Key.DealerName,
                                   Status = tempGroups.Key.Status,
                                   ReviewStatus = tempGroups.Key.ReviewStatus,
                                   MarketingDepartmentName = tempGroups.Key.MarketingDepartmentName,
                                   MarketingResponsibleName = tempGroups.Key.MarketingResponsibleName,
                                   ActualerCoachName = tempGroups.Key.ActualerCoachName,
                                   ActualCoachTime = tempGroups.Key.ActualCoachTime,
                                   //對應的月度輔導任務項目清單中標准分值 
                                   ScoreTotal = tempGroups.Sum(r => r.StandardScore ?? 0),
                                   NoQualifiedQty = tempGroups.Sum(r => r.IsQualified == false ? 1 : 0)
                               };
            //獲取分組后的結果         
            var totalCount = await queryResults.CountAsync();
            var pagedResults = await queryResults.ProjectTo<GetCoachTaskDetailOutput>(_autoMapper.ConfigurationProvider).PageAndOrderBy(pageRequest).ToListAsync();
            pagedResults.ForEach(item => {
                if (item.ScoreTotal < 0) {
                    item.ScoreTotal = 0;
                }
            });
            return new Page<GetCoachTaskDetailOutput>(pageRequest, totalCount, pagedResults);
        }

  上面的寫法非常簡單就是三張表進行join然后進行分組,只不過是最終的結果中ScoreTotal和NoQualifiedQty需要根據分組的結果進行求和,對於這種比較簡單的分組結果EFCore是完全能夠生成正確的SQL,我們來看看此時EFCore正確生成的SQL。

SELECT TOP (20)
  [coachTaskDetail].[ActualCoachTime],
  [coachTaskDetail].[ActualerCoachName],
  [coachTask].[Code],
  [coachTaskDetail].[DealerCode],
  COALESCE([coachTaskDetail].[Id], '00000000-0000-0000-0000-000000000000') AS [Id],
  [coachTask].[MarketingDepartmentName],
  [coachTask].[MarketingResponsibleName],
  [coachTask].[Month],
  COALESCE(SUM(CASE
               WHEN [coachTaskItemDetail].[IsQualified] = 0
                 THEN 1
               ELSE 0
               END), 0)                                                    AS [NoQualifiedQty],
  [coachTask].[Quarter],
  COALESCE([coachTaskDetail].[ReviewStatus], 0)                            AS [ReviewStatus],
  COALESCE(SUM(COALESCE([coachTaskItemDetail].[StandardScore], 0.0)), 0.0) AS [ScoreTotal],
  [coachTaskDetail].[DealerName]                                           AS [ShortName],
  COALESCE([coachTaskDetail].[Status], 0)                                  AS [Status],
  COALESCE([coachTask].[Year], 0)                                          AS [Year]
FROM [CoachTask] AS [coachTask]
  INNER JOIN [CoachTaskDetail] AS [coachTaskDetail] ON [coachTask].[Id] = [coachTaskDetail].[CoachTaskId]
  INNER JOIN [CoachTaskItemDetail] AS [coachTaskItemDetail]
    ON [coachTaskDetail].[Id] = [coachTaskItemDetail].[CoachTaskDetailId]
GROUP BY [coachTaskDetail].[Id], [coachTask].[Code], [coachTask].[Year], [coachTask].[Quarter], [coachTask].[Month],
  [coachTaskDetail].[DealerCode], [coachTaskDetail].[DealerName], [coachTaskDetail].[Status],
  [coachTaskDetail].[ReviewStatus], [coachTask].[MarketingDepartmentName], [coachTask].[MarketingResponsibleName],
  [coachTaskDetail].[ActualerCoachName], [coachTaskDetail].[ActualCoachTime]
ORDER BY [coachTask].[Code] DESC

  未完,待續......


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM