在LINQ中使用左外连接重新创建大型复杂查询

时间:2016-04-28 23:14:20

标签: 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.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,而statusIds最终也为null。

我确信有更好的方法可以做到这一点。我愿意接受建议。

作为一个说明,是的,我知道TSQL上与“With(NoLock)”选项相关的风险,我们是一个非常繁忙的数据库上的辅助应用程序,我们不想创建阻塞链计费和支持人员开展工作。对于我们正在做的事情,NoLock不是问题。这不像我们正在使用聚合器进行多个查询联合。 ;)

1 个答案:

答案 0 :(得分:1)

嗯,你有很多左外连接,并且LINQ中自然不支持左外连接,但是模式是众所周知的 - 看看左外加入join clause (C# Reference)中的复合键部分,所以您只需遵循以下模式:

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)

要考虑的一个细节是在从左外连接的right部分选择不可为空的字段时包含可空的强制转换。例如,如果StatusId类型为int,那么您需要使用以下内容:

select new 
{
    workingAddressStatusId = (int?)wa.StatusId,
    // ...
}