当我引入GroupJoin时,我遇到的问题只是拉动查询中指定的列。为简单起见,我将代码缩小到了问题的范围,但还有很多其他的连接。由于完整语句的复杂性,我非常希望不要使用Union!= null to == null,不得不一遍又一遍地编写基本相同的代码......
使用标准连接:
var ret = await _ICMDbContext.CaseHeader
.Join(_ICMDbContext.Employee, ch => ch.EmployeeIdRecFkey, emp => emp.IdRec, (ch, emp) => new { ch, emp })
.Where(x=> x.ch.IsDeleted.Equals(false))
.Select(x => new CaseHeaderGridModel()
{
IdRec = x.ch.IdRec,
CaseCategoryId = x.ch.CaseCategoryId,
CreateDttm = x.ch.CreateDttm,
CreateUser = x.ch.CreateUser,
EmployeeIdRecFkey = x.ch.EmployeeIdRecFkey,
EmployeeName = x.ch.EmployeeName,
EmployeeClientOrgIdRecFkey = x.ch.EmployeeClientOrgIdRecFkey,
CaseEmployerName = x.ch.EmployerName,
OccurrenceTreatmentState = x.ch.EventState,
CallItemAssignedUserObjectId = x.ch.CallItemAssignedUserObjectId,
CallItemOriginatorUserObjectId = x.ch.CallItemOriginatorUserObjectId.Value,
CaseItemLocator = x.ch.CaseItemLocator,
CaseStatusId = x.ch.CaseStatusId,
RequiredLanguageId = x.ch.RequiredLanguageId,
NameFirst = x.emp.NameFirst,
NameMiddle = x.emp.NameMiddle,
NameLast = x.emp.NameLast
})
.ToListAsync();
生成仅具有请求列的预期SQL:
SELECT [ch].[IdRec], [ch].[CaseCategoryId], [ch].[CreateDttm], [ch].[CreateUser], [ch].[EmployeeIdRecFkey], [ch].[EmployeeName]
, [ch].[EmployeeClientOrgIdRecFkey], [ch].[EmployerName] AS [CaseEmployerName], [ch].[EventState] AS [OccurrenceTreatmentState]
, [ch].[CallItemAssignedUserObjectId], [ch].[CallItemOriginatorUserObjectId], [ch].[CaseItemLocator], [ch].[CaseStatusId]
, [ch].[RequiredLanguageId], [emp].[NameFirst], [emp].[NameMiddle], [emp].[NameLast]
FROM [CaseHeader] AS [ch]
INNER JOIN [Employee] AS [emp] ON [ch].[EmployeeIdRecFkey] = [emp].[IdRec]
WHERE [ch].[IsDeleted] = 0
但是,当我把它变成GroupJoin时(可能没有链接的员工,需要一个Left):
var ret = await _ICMDbContext.CaseHeader
.GroupJoin(_ICMDbContext.Employee, ch => ch.EmployeeIdRecFkey, emp => emp.IdRec, (ch, emp) => new { ch, emp = emp.FirstOrDefault() })
.Where(x=> x.ch.IsDeleted.Equals(false))
.Select(x => new CaseHeaderGridModel()
{
IdRec = x.ch.IdRec,
CaseCategoryId = x.ch.CaseCategoryId,
CreateDttm = x.ch.CreateDttm,
CreateUser = x.ch.CreateUser,
EmployeeIdRecFkey = x.ch.EmployeeIdRecFkey,
EmployeeName = x.ch.EmployeeName,
EmployeeClientOrgIdRecFkey = x.ch.EmployeeClientOrgIdRecFkey,
CaseEmployerName = x.ch.EmployerName,
OccurrenceTreatmentState = x.ch.EventState,
CallItemAssignedUserObjectId = x.ch.CallItemAssignedUserObjectId,
CallItemOriginatorUserObjectId = x.ch.CallItemOriginatorUserObjectId.Value,
CaseItemLocator = x.ch.CaseItemLocator,
CaseStatusId = x.ch.CaseStatusId,
RequiredLanguageId = x.ch.RequiredLanguageId,
NameFirst = x.emp.NameFirst,
NameMiddle = x.emp.NameMiddle,
NameLast = x.emp.NameLast
})
.ToListAsync();
我从两个表中得到每一列(所有表都是现实的)
SELECT [ch].[IdRec] AS [IdRec0], [ch].[AdditionalData], [ch].[BillToClientOrgIdRecFkey], [ch].[CallGroupLocator], [ch].[CallItemAssignedUserObjectId]
, [ch].[CallItemLocator], [ch].[CallItemOriginatorUserObjectId], [ch].[CallItemTypeId], [ch].[CallerName], [ch].[CallerOrganization], [ch].[CaseCategoryId]
, [ch].[CaseItemContacts], [ch].[CaseItemLocator], [ch].[CaseOutcomeWithIntervention_OutcomeId], [ch].[CaseOutcomeWithoutIntervention_OutcomeId]
, [ch].[CaseStatusId], [ch].[CaseWorkflowPhaseId], [ch].[ChangeData], [ch].[CreateDttm] AS [CreateDttm0], [ch].[CreateUser] AS [CreateUser0]
, [ch].[CurrentMedicalStatusCalc], [ch].[DateOfBirth], [ch].[EmployeeClientOrgIdRecFkey], [ch].[EmployeeIdRecFkey], [ch].[EmployeeName]
, [ch].[EmployeePhoneNumber], [ch].[EmployerName] AS [CaseEmployerName], [ch].[EventCity], [ch].[EventDateTime], [ch].[EventDateTimeQualifierId]
, [ch].[EventDescription], [ch].[EventElevatedAdvice], [ch].[EventGeographicDescription], [ch].[EventState] AS [OccurrenceTreatmentState], [ch].[GenderId]
, [ch].[GeneratedFromVoicemail], [ch].[HandednessId], [ch].[IsDeleted], [ch].[JobDutiesDescription], [ch].[JobPhysicalityClassificationId]
, [ch].[LocalClientOrgId], [ch].[LocaleId], [ch].[MedicalHistory], [ch].[NoteData], [ch].[PhoneCallerIdExt], [ch].[PhoneCallerIdNumber]
, [ch].[PhoneStatedCallBackExt], [ch].[PhoneStatedCallBackNumber], [ch].[RequiredLanguageId], [ch].[ServiceCategoryId], [ch].[ServiceOrgNotifiedDispositionId]
, [ch].[SysRowVersion], [ch].[SysTag], [ch].[ThirdPartyEmployer], [ch].[ThirdPartyEmployerIdRecFkey], [ch].[TimezoneId], [ch].[TreatmentCity]
, [ch].[TreatmentState], [ch].[UpdateDttm], [ch].[UpdateUser], [ch].[WorkDaysCalcLostTime], [ch].[WorkDaysCalcRestricted], [ch].[WorkScheduleDescription]
, [ch].[WorkdayDateEstimatedReturn], [ch].[WorkdayDateFirstMissed], [ch].[WorkdayDateFullReturn], [ch].[WorkdayDateRestrictedReturn], [ch].[WorkloadMetricValue]
, [emp].[IdRec], [emp].[AdditionalData], [emp].[ClientEmployeeIdentity], [emp].[CostCenter], [emp].[CreateDttm], [emp].[CreateUser], [emp].[DateOfBirth]
, [emp].[DispositionId], [emp].[EmploymentEndDate], [emp].[EmploymentStartDate], [emp].[ExternalReferenceEmployeeId], [emp].[ExternalReferenceEmployeeId_Source]
, [emp].[GenderId], [emp].[HandednessId], [emp].[IdRecParent], [emp].[IsDeleted], [emp].[JobPhysicalityClassificationId], [emp].[JobTitleId], [emp].[LanguageId]
, [emp].[LocalClientOrgId], [emp].[NameFirst], [emp].[NameFirstPreferred], [emp].[NameGenerationalTitleId], [emp].[NameLast], [emp].[NameMiddle]
, [emp].[NameProfessionalTitle], [emp].[NameTitleId], [emp].[NationalOrStateIdNumber], [emp].[PersonnelLocatorCode], [emp].[SysRowVersion], [emp].[SysTag]
, [emp].[ThirdPartyEmployerName], [emp].[ThirdPartyEmployerNameIdRecFkey], [emp].[UpdateDttm], [emp].[UpdateUser], [emp].[WorkerClassificationId]
FROM [CaseHeader] AS [ch]
LEFT JOIN [Employee] AS [emp] ON [ch].[EmployeeIdRecFkey] = [emp].[IdRec]
WHERE [ch].[IsDeleted] = 0
ORDER BY [ch].[EmployeeIdRecFkey]
如上所述,这实际上是一个更复杂的查询,并且其中有多个GroupJoins - 使用GroupJoins时实际返回的列数为数百,而我们的数据集相当大,因此这导致我们的查询需要花费15-20秒才能返回,而如果我将列限制为仅我要求的列,则返回时间不到一秒。
有没有办法通过GroupJoin(或其他方式获得左连接)来解决这个问题,或者我是否需要Union并创建一堆分支?
从上下文中选择CaseHeaderModel,然后在Model上执行GroupJoin而不是上下文不会更改生成的SQL。 GroupJoin似乎真的想抓住所有专栏。
编辑 我已经尝试了下面的代码,不幸的是它仍然包含所有列:
var ret = _ICMDbContext.CaseHeader
.GroupJoin(_ICMDbContext.Employee, ch => ch.EmployeeIdRecFkey, emp => emp.IdRec, (ch, emp) => new
{
ch = new
{
ch.IdRec,
ch.CaseCategoryId,
ch.CaseStatusId,
ch.IsDeleted,
ch.CreateDttm,
ch.CreateUser,
ch.CallItemTypeId,
ch.EmployeeIdRecFkey,
ch.EmployeeName,
ch.EmployeeClientOrgIdRecFkey,
ch.EmployerName,
ch.EventState,
ch.CallItemAssignedUserObjectId,
ch.CallItemOriginatorUserObjectId,
ch.CaseItemLocator,
ch.RequiredLanguageId
}
,
emp = emp.Select(x => new
{
x.NameFirst,
x.NameMiddle,
x.NameLast
}).FirstOrDefault()
})
.Where(x=> x.ch.EmployeeIdRecFkey != null && x.ch.IsDeleted.Equals(false))
.Select(x => new CaseHeaderGridModel()
{
IdRec = x.ch.IdRec,
CaseCategoryId = x.ch.CaseCategoryId,
CreateDttm = TimeZoneConverter.GetConvertFromDateToDate(x.ch.CreateDttm, utcAdditionalData, loggedInUserTimeZoneAddlData).Value,
CreateDttmValue = TimeZoneConverter.GetConvertFromDateToDateFormattedTimeString(x.ch.CreateDttm, utcAdditionalData, loggedInUserTimeZoneAddlData),
CreateTimezoneShortName = TimeZoneConverter.GetTimeZoneShortName(x.ch.CreateDttm, loggedInUserTimeZoneAddlData),
CreateUser = x.ch.CreateUser,
CallItemTypeId = x.ch.CallItemTypeId.Value,
EmployeeIdRecFkey = x.ch.EmployeeIdRecFkey,
EmployeeName = x.ch.EmployeeName,
EmployeeClientOrgIdRecFkey = x.ch.EmployeeClientOrgIdRecFkey,
CaseEmployerName = x.ch.EmployerName,
OccurrenceTreatmentState = x.ch.EventState,
CallItemAssignedUserObjectId = x.ch.CallItemAssignedUserObjectId,
CallItemOriginatorUserObjectId = x.ch.CallItemOriginatorUserObjectId.Value,
CaseItemLocator = x.ch.CaseItemLocator,
RequiredLanguageId = x.ch.RequiredLanguageId,
NameFirst = x.emp.NameFirst,
NameMiddle = x.emp.NameMiddle,
NameLast = x.emp.NameLast,
CaseStatusId = x.ch.CaseStatusId,
}).ToList();
我得到了相同的:
SELECT [ch].[IdRec] AS [IdRec0], [ch].[AdditionalData], [ch].[BillToClientOrgIdRecFkey], [ch].[CallGroupLocator], [ch].[CallItemAssignedUserObjectId]
, [ch].[CallItemLocator], [ch].[CallItemOriginatorUserObjectId], [ch].[CallItemTypeId], [ch].[CallerName], [ch].[CallerOrganization], [ch].[CaseCategoryId]
, [ch].[CaseItemContacts], [ch].[CaseItemLocator], [ch].[CaseOutcomeWithIntervention_OutcomeId], [ch].[CaseOutcomeWithoutIntervention_OutcomeId]
, [ch].[CaseStatusId], [ch].[CaseWorkflowPhaseId], [ch].[ChangeData], [ch].[CreateDttm], [ch].[CreateUser] AS [CreateUser0]
, [ch].[CurrentMedicalStatusCalc], [ch].[DateOfBirth], [ch].[EmployeeClientOrgIdRecFkey], [ch].[EmployeeIdRecFkey], [ch].[EmployeeName]
, [ch].[EmployeePhoneNumber], [ch].[EmployerName] AS [CaseEmployerName], [ch].[EventCity], [ch].[EventDateTime], [ch].[EventDateTimeQualifierId]
, [ch].[EventDescription], [ch].[EventElevatedAdvice], [ch].[EventGeographicDescription], [ch].[EventState] AS [OccurrenceTreatmentState], [ch].[GenderId]
, [ch].[GeneratedFromVoicemail], [ch].[HandednessId], [ch].[IsDeleted], [ch].[JobDutiesDescription], [ch].[JobPhysicalityClassificationId]
, [ch].[LocalClientOrgId], [ch].[LocaleId], [ch].[MedicalHistory], [ch].[NoteData], [ch].[PhoneCallerIdExt], [ch].[PhoneCallerIdNumber]
, [ch].[PhoneStatedCallBackExt], [ch].[PhoneStatedCallBackNumber], [ch].[RequiredLanguageId], [ch].[ServiceCategoryId]
, [ch].[ServiceOrgNotifiedDispositionId], [ch].[SysRowVersion], [ch].[SysTag], [ch].[ThirdPartyEmployer], [ch].[ThirdPartyEmployerIdRecFkey]
, [ch].[TimezoneId], [ch].[TreatmentCity], [ch].[TreatmentState], [ch].[UpdateDttm], [ch].[UpdateUser], [ch].[WorkDaysCalcLostTime]
, [ch].[WorkDaysCalcRestricted], [ch].[WorkScheduleDescription], [ch].[WorkdayDateEstimatedReturn], [ch].[WorkdayDateFirstMissed]
, [ch].[WorkdayDateFullReturn], [ch].[WorkdayDateRestrictedReturn], [ch].[WorkloadMetricValue], [emp].[IdRec], [emp].[AdditionalData]
, [emp].[ClientEmployeeIdentity], [emp].[CostCenter], [emp].[CreateDttm], [emp].[CreateUser], [emp].[DateOfBirth], [emp].[DispositionId]
, [emp].[EmploymentEndDate], [emp].[EmploymentStartDate], [emp].[ExternalReferenceEmployeeId], [emp].[ExternalReferenceEmployeeId_Source]
, [emp].[GenderId], [emp].[HandednessId], [emp].[IdRecParent], [emp].[IsDeleted], [emp].[JobPhysicalityClassificationId], [emp].[JobTitleId]
, [emp].[LanguageId], [emp].[LocalClientOrgId], [emp].[NameFirst], [emp].[NameFirstPreferred], [emp].[NameGenerationalTitleId], [emp].[NameLast]
, [emp].[NameMiddle], [emp].[NameProfessionalTitle], [emp].[NameTitleId], [emp].[NationalOrStateIdNumber], [emp].[PersonnelLocatorCode]
, [emp].[SysRowVersion], [emp].[SysTag], [emp].[ThirdPartyEmployerName], [emp].[ThirdPartyEmployerNameIdRecFkey], [emp].[UpdateDttm], [emp].[UpdateUser]
, [emp].[WorkerClassificationId]
FROM [CaseHeader] AS [ch]
LEFT JOIN [Employee] AS [emp] ON [ch].[EmployeeIdRecFkey] = [emp].[IdRec]
WHERE [ch].[EmployeeIdRecFkey] IS NOT NULL AND ([ch].[IsDeleted] = 0)
ORDER BY [ch].[EmployeeIdRecFkey]
问题是没有使用SelectMany。当我总是有1个匹配时,我没有意识到这是必需的,并且只使用左连接来覆盖空值。我认为在GroupJoin中放置FirstOrDefault()会做同样的事情。添加SelectMany时,会正确选择列。