LINQ与SQL连接和分组(具体案例)

时间:2015-12-07 20:33:07

标签: sql linq join grouping

在SQL中,当您进行一系列连接时,它会将所有连接对象视为一个“超级对象”进行选择。只要您在分组中包含您选择的任何内容(除非它是由分组生成的,例如汇总一堆int列),当您按特定列分组时,情况仍然如此。

在LINQ中,您可以连续地执行一系列连接,并从中进行选择。但是,执行分组时,其行为会有所不同。查询样式LINQ中的语法只允许对单个表(即其中一个连接)进行分组,丢弃其他表。

对于一个示例,假设我们有几个表:

Request
-------
int ID (PK)
datetime Created
int StatusID (FK)

Item
----
int ID (PK)
string Name

RequestItem
-----------
int ID (PK)
int ItemID (FK)
int RequestID (FK)
int Quantity

Inventory
---------
int ID (PK)
int ItemID (FK)
int Quantity    

LU_Status
---------
int ID (PK)
string Description

在我们的示例中,LU_Status在数据库中有三个值:

1 - New
2 - Approved
3 - Completed

这是实际情况的简化版本,引导我提出这个问题。给定此模式,需要生成一个报告,显示请求的项目数(状态不是“已完成”),已批准的项目(状态为“已批准”),分发的项目(状态为“已完成”)以及项目数库存(来自库存),全部按项目分组。如果这有点模糊,请看一下SQL或让我知道,我会尽量让它更清楚。

在SQL中我可能会这样做:

select i.Name,
Requested = sum(ri.Quantity), 
Approved = sum(case when r.StatusID = 2 then ri.Quantity else 0 end)
Distributed = sum(case when r.StatusID = 3 then ri.Quantity else 0 end)
Storage = sum(Storage)
from RequestItem as ri
inner join Request as r on r.ID = ri.RequestID
inner join Item as i on i.ID = ri.ItemID
inner join (select ItemID, Storage = sum(Quantity)
            from Inventory
            group by ItemID)
            as inv on inv.ItemID = ri.ItemID
group by i.Name

这会产生所需的结果。

我开始在LINQ中重写它,并且到目前为止:

var result = from ri in RequestItem
             join r in Request on ri.RequestID equals r.ID
             join i in Item on ri.ItemID equals i.ID
             join x in (from inv in Inventory
                        group inv by inv.ItemID into g
                        select new { ItemID = g.Key, Storage = g.Sum(x => x.Quantity) })
                        on ri.ItemID equals x.ItemID
             group...????

此时一切进展顺利,但我意识到我不能简单地按照i.Name分组,就像我在SQL中所做的那样。实际上,似乎没有办法将所有连接的东西组合在一起,以便我可以从中选择必要的东西,所以我被迫停在那里。我理解如何在更简单的情况下使用组语法(参见子查询),但如果有一种方法可以在LINQ中进行这种分组,我没有看到它,在这里和其他地方搜索并没有照亮我。

这是LINQ的缺点,还是我错过了什么?

2 个答案:

答案 0 :(得分:3)

您可以在包含所需数据的分组中创建匿名类型:

var result = from ri in RequestItem
             join r in Request on ri.RequestID equals r.ID
             join i in Item on ri.ItemID equals i.ID
             join x in (from inv in Inventory
                        group inv by inv.ItemID into g
                        select new { ItemID = g.Key, Storage = g.Sum(x => x.Quantity) })
                        on ri.ItemID equals x.ItemID
             group new
             {
                 i.Name,
                 r.StatusId,
                 ri.Quantity,
                 x.Storage,
             }
             by i.Name into grp
             select new
             {
                 grp.Key,
                 Requested = grp.Where(x => x.StatusID == 2).Sum(x => x.Quantity),
                 Distributed = grp.Where(x => x.StatusID == 3).Sum(x => x.Quantity),
                 Storage = grp.Sum(x => x.Storage)
             }

(未经测试,显然,但它应该接近)。

答案 1 :(得分:2)

最简单的方法是使用group new { ... } by ...构造,并在{ ... }中包含稍后需要的联接中的所有项目,如下所示

var query =
    from ri in db.RequestItem
    join r in db.Request on ri.RequestID equals r.ID
    join i in db.Item on ri.ItemID equals i.ID
    join x in (from inv in db.Inventory
               group inv by inv.ItemID into g
               select new { ItemID = g.Key, Storage = g.Sum(x => x.Quantity) }
              ) on ri.ItemID equals x.ItemID
    group new { ri, r, i, x } by i.Name into g
    select new
    {
        Name = g.Key,
        Requested = g.Sum(e => e.ri.Quantity),
        Approved = g.Sum(e => e.r.StatusID == 2 ? e.ri.Quantity : 0),
        Distributed = g.Sum(e => e.r.StatusID == 3 ? e.ri.Quantity : 0),
        Storage = g.Sum(e => e.x.Storage)
    };