C# 在LINQ中使用左外部联接重新创建大型复杂查询
我在寻找一些解决这个问题的建议。我们有一个大型SQL查询,其中包含许多左外部联接 以下是查询:C# 在LINQ中使用左外部联接重新创建大型复杂查询,c#,entity-framework,linq,tsql,entity-framework-6,C#,Entity Framework,Linq,Tsql,Entity Framework 6,我在寻找一些解决这个问题的建议。我们有一个大型SQL查询,其中包含许多左外部联接 以下是查询: Select Top 1 WA.BHATUS_ID, WA.[CRMBHG_WORKINGACCOUNT_ID], BA.BHATUS_ID, BA.[CRMBHG_BILLINGACCOUNT_ID], BAD.BHATUS_ID, [BAD].[CRMBHG_BILLINGADDRESS_ID], SAD.BHATUS_ID, [SAD].[CRMBHG_SERVICEADDRESS_ID], C
Select Top 1 WA.BHATUS_ID, WA.[CRMBHG_WORKINGACCOUNT_ID], BA.BHATUS_ID, BA.[CRMBHG_BILLINGACCOUNT_ID], BAD.BHATUS_ID, [BAD].[CRMBHG_BILLINGADDRESS_ID], SAD.BHATUS_ID, [SAD].[CRMBHG_SERVICEADDRESS_ID], C.BHATUS_ID, [C].[CRMBHG_CONTACT_ID]
From BillingHiBHory As BH With (NoLock)
Left Outer Join WorkingAccount As WA With (NoLock)
On WA.UniversalAccountId = BH.UniversalAccountId
And WA.BillingRegion = BH.BillingRegion
Left Outer Join BillingAccount As BA With (NoLock)
On BA.UniversalAccountId = BH.UniversalAccountId
And BA.BillingRegion = BH.BillingRegion
Left Outer Join BillingAddress As BAD With (NoLock)
On BAD.Grouping = BH.Grouping
And BAD.SubGrouping = BH.SubGrouping
And BAD.BillingRegion = BH.BillingRegion
Left Outer Join dbo.SubScriberInfo SUB With (NoLock)
On SUB.Grouping = BH.Grouping
And SUB.SubGrouping = BH.SubGrouping
And SUB.BillingRegion = BH.BillingRegion
Left Outer Join ServiceAddress As SAD With (NoLock)
On SAD.ControlNumberAssigned = SUB.ServceControlNum
And SAD.BillingRegion = SUB.BillingRegion
Left Outer Join Contact As C With (NoLock)
On BH.AccountNum = C.AccountNum
And BH.BillingRegion = C.BillingRegion
Where BH.Grouping = @Grouping
And BH.SubGrouping = @SubGrouping
Order By BH.SequenceOrder, WA.StatusDate Desc, BA.StatusDate Desc, BAD.StatusDate Desc, SAD.StatusDate Desc, C.StatusDate Desc
这是我第一次尝试LINQ语法
var statusIds = (
from wa in db.WorkingAccount
from ba in db.BillingAccount
from bad in db.BillingAddress
from si in db.SubscriberInfo
from sad in db.ServiceAddress
from c in db.CRMSTG_CONTACT
where wa.UniversalAccountId == UniversalSubscriberId && wa.BillingRegion == BillingRegion
where ba.UniversalAccountId == UniversalSubscriberId && ba.BillingRegion == BillingRegion
where bad.Grouping == Grouping && bad.SubGrouping == SubGrouping && bad.BillingRegion == BillingRegion
where si.Grouping == Grouping && si.SubGrouping == SubGrouping && si.BillingRegion == BillingRegion
where sad.ControlNumberAssigned == si.ServceControlNum && sad.BillingRegion == BillingRegion
where c.AccountNum == AccountNumber && c.BillingRegion == BillingRegion
orderby wa.StatusDate descending, ba.StatusDate descending, bad.StatusDate descending, sad.StatusDate descending, c.StatusDate descending
select new
{
workingAddressStatusId = wa.StatusId,
billingAccountStatusId = ba.StatusId,
billingAddressStatusId = bad.StatusId,
serviceAddressStatusId = sad.StatusId,
contactStatusId = c.StatusDate
}
).FirstOrDefault();
这主要是有效的,但感觉像一个大黑客。当其中一个StatusId值返回为null,并且StatusId最终也是null时,就会出现这种情况
我相信有更好的办法。我愿意接受建议
请注意,是的,我知道TSQL上“with(NoLock)”选项的相关风险,我们是一个非常繁忙的数据库上的辅助应用程序,我们不想在计费和支持人员工作时创建阻塞链。就我们所做的而言,NoLock不是问题。这不像我们使用聚合器进行多个查询联合。;) 的确,您有很多左外连接,而且左外连接在LINQ中不受自然支持,但该模式是众所周知的-请查看中的左外连接和组合键部分,因此您只需遵循该模式即可:
from bh in db.BillingHiBHory
join wa in db.WorkingAccount
on new { bh.UniversalAccountId, bh.BillingRegion }
equals new { wa.UniversalAccountId, wa.BillingRegion }
into bh_wa from wa in bh_wa.DefaultIfEmpty() // this turns the above (inner) join into left outer join
join ba in db.BillingAccount
on new { bh.UniversalAccountId, bh.BillingRegion }
equals new { ba.UniversalAccountId, ba.BillingRegion }
into bh_ba from ba in bh_ba.DefaultIfEmpty()
join bad in db.BillingAddress
on new { bh.Grouping, bh.SubGrouping, bh.BillingRegion }
equals new { bad.Grouping, bad.SubGrouping, bad.BillingRegion }
into bh_bad from bad in bh_bad.DefaultIfEmpty()
join si in db.SubscriberInfo
on new { bh.Grouping, bh.SubGrouping, bh.BillingRegion }
equals new { si.Grouping, si.SubGrouping, si.BillingRegion }
into bh_si from si in bh_si.DefaultIfEmpty()
join sad in db.ServiceAddress
on new { si.ServceControlNum, si.BillingRegion }
equals new { sad.ControlNumberAssigned, sad.BillingRegion }
into si_sad from sad in si_sad.DefaultIfEmpty()
join c in db.CRMSTG_CONTACT
on new { bh.AccountNum, bh.BillingRegion }
equals new { c.AccountNum, c.BillingRegion }
into bh_c from c in bh_c.DefaultIfEmpty()
where bh.Grouping == Grouping
&& bh.SubGrouping == SubGrouping
// ... (the rest)
一个细节要考虑的是,在从代码< >右< /代码>左外连接的一部分中选择不可空字段时,包括可空抛出。例如,如果
StatusId
类型为int
,则需要使用如下内容:
select new
{
workingAddressStatusId = (int?)wa.StatusId,
// ...
}
你好,你好,如果可能的话,你能把每一个的域结构都包括进去吗?哦,在清理并移动到MSSQL数据库之前,从AS400数据库到暂存MSSQL数据库的数据类型不一致。让我写下我们正在链接的键的要点。在我看来,如果这是对遗留数据库的部分重构,我会将此查询保存在存储过程中,并让EF调用sp,而不是试图欺骗linq为您生成此特定sql。然后,我将通过调整数据模型来解决多个左连接,使其不再需要这么多。不幸的是,我们没有重构遗留数据库。AS400是一个大而活跃的数据组件,它管理一些核心业务流程,因此短期内不会改变。我有一部分是说我应该在ADO.Net记录集下以批处理SQL的形式运行它,并称之为好的,但我想在LINQ中解决这个问题。我本可以请求获得将存储过程添加到生产服务器并导入该函数的权限,但请参见上面的兴趣:)您的代码示例在匿名类型中有bug;它们应该包括成员名称。我有一个超时的问题,它回来的非常快,但这个答案解决了这个问题<代码>新建{grouping=bh.grouping,subGrouping=bh.subGrouping,…另外,使用匿名类型来执行和等于操作是非常巧妙的。我没有想到这一点。虽然我确实了解到您的成员名称必须相同,否则您将在编译时出错。实际上,匿名类型字段的名称不需要提供大多数情况下都是明明白白的和推断出来的。我只是忘记了显式地命名一个具有不同名称的
si_sad
连接字段,因为是的-如果字段的名称和类型都匹配,则两个匿名类型被认为是一个相同的类型。让我更清楚一点。…在新的{value1=db1.value1,value2=db2.value2}等于new{value1=db2.value1,value2=db2.value2}
将编译,而…在new{value1=db1.value1,value2=db2.value2}上等于new{valueA=db2.value1,valueB=db2.value2}
将不编译。