如何选择“产品”行而不是“销售”行

时间:2019-07-12 07:51:58

标签: c# .net linq

在下面的LINQ查询中,我想选择“产品”行并将“销售”行数据添加到其中,但事实恰恰相反,它是在选择“销售”行并添加“产品”行

var query = (from product in SampleProductTable
from sale in SampleSalesTable
where (sale.ProductId == product.Id)
select new
{
    Id = product.Id,
    TotalSales = product.TotalSales + ((product.Id == sale.ProductId) ? sale.Amount : 0)
})

样品表

+-------+------------+---------+-----------------+-------+------------+
|  Id   | CategoryId | BrandId |      Name       | Price | TotalSales |
+-------+------------+---------+-----------------+-------+------------+
| mlk3  | MLK        | BRND1   | Creamy Milk     |     5 |         10 |
| snck2 | SNCK       | BRND2   | Chocolate Snack |     2 |         24 |
+-------+------------+---------+-----------------+-------+------------+

示例销售表

+-----+-----------+--------+
| Id  | ProductId | Amount |
+-----+-----------+--------+
| 120 | mlk3      |     55 |
| 121 | mlk3      |     15 |
| 122 | snck2     |     12 |
| 123 | mlk3      |      5 |
| 124 | mlk3      |     10 |
| 125 | snck2     |      2 |
| 126 | mlk3      |    115 |
| 127 | snck2     |      6 |
| 128 | snck2     |     34 |
+-----+-----------+--------+

所需的输出

+-------+------------+
|  Id   | TotalSales |
+-------+------------+
| mlk3  |        210 |
| snck2 |         78 |
+-------+------------+

6 个答案:

答案 0 :(得分:3)

var answer = (from product in SampleProductTable
              join sale in SampleSalesTable on product.Id == sale.ProductId into subSales
              from subSale in subSales.DefaultIfEmpty()
              group subSale by new { product.Id, product.TotalSales } into gr
              select new 
              {
                  gr.Key.Id,
                  TotalSales = gr.Sum(x => x == null ? 0 : x.Amount) + gr.Key.TotalSales
              }).ToList();

近似T-SQL:

select p.Id, p.TotalSales + sum(coalesce(s.Amount, 0)) TotalSales 
from SampleProductTable p
left outer join SampleSalesTable s on p.Id = s.ProductId 
group by p.Id, p.TotalSales

答案 1 :(得分:1)

在您的示例中,您将两个集合连接在一起,结果将具有与唯一的子记录(在这种情况下为销售额)一样多的行,并为每个记录创建一个新对象(类似于INNER JOIN)。这就是为什么结果是“基于销售”的原因。

如果我正确理解了您的意图,我会采取以下方式:

SampleProductTable.Select(p => new 
{
    Id = p.Id,
    TotalSales = p.Sales.Sum(s => s.Amount)
}

请注意,对于这种方法,您将需要在产品上映射“销售”系列。

答案 2 :(得分:1)

首先最好改用join语句,然后似乎需要根据SalesProductId表进行分组:

 var query = (from product in SampleProductTable
              join sale in SampleSalesTable.GroupBy(c => c.ProductId) 
              on product.Id equals sale.Key
              select new
              {
                  Id = product.Id,
                  TotalSales = product.TotalSales + sale.Sum(c=>c.Amount)
              }).ToList();

还请注意:由于您在代码中使用了where语句,因此您不再需要在(product.Id == sale.ProductId) ?中使用此条件select。与我的相同,因为我使用了带有on关键字的join语句,所以不需要在选择区域中使用条件。

您可以在以下链接中看到所需的结果:

https://dotnetfiddle.net/RFTtrv

答案 3 :(得分:1)

通常,LINQ术语将您要查找的查询形状称为grouped join

  

组联接对于生成分层数据结构很有用。它将第一个集合中的每个元素与第二个集合中的一组相关元素配对。

对于您而言,它将为每个Sales生成一组相关的Product。然后,您只需要在最终投影(Sum)内应用聚合(select):

var query =
    from product in SampleProductTable
    join sale in SampleSalesTable on product.Id equals sale.ProductId into productSales
    select new
    {
        Id = product.Id,
        TotalSales = product.TotalSales + productSales.Sum(sale => sale.Amount)
    };

但是由于您在某些评论中提到要转换为SQL,因此很可能您正在使用某些ORM,例如LinqToSQL,EF或EF Core。在这种情况下,事情就更简单了。这些ORM支持表示关系的所谓导航属性,并且在内部查询中使用所有必需的联接转换为SQL时,因此您不必为此类细节而烦恼,并且可以集中精力产生预期结果所需的逻辑。

在这种情况下,Product类通常具有类似的内容

public ICollection<Sale> Sales { get; set; }

,这样的查询将很简单Select,如下所示:

var query = db.Products
    .Select(product => new
    {
        Id = product.Id,
        TotalSales = product.TotalSales + product.Sales.Sum(sale => sale.Amount)
    });

答案 4 :(得分:1)

LEFT JOIN和分组看起来像

        var query = 
            from product in SampleProductTable
            join sale in SampleSalesTable.GroupBy(c => c.ProductId) 
                 on product.Id equals sale.Key into join1
            from lj in join1.DefaultIfEmpty() // left join 
            select new
            {
                Id = product.Id,
                TotalSales = product.TotalSales + (lj == null ? 0 : lj.Sum(c => c.Amount))
            };

Left join可能返回null,因此在尝试对潜在组lj求和之前,请先对其进行检查。对于更高的C#版本,可以将null检查缩写为

            TotalSales = product.TotalSales + (lj?.Sum(c => c.Amount) ?? 0)

Fiddle

答案 5 :(得分:0)

在查询语法中,Slava的解决方案应返回您要查找的结果,即

var methodSyntax = (SampleProductTable
    .GroupJoin(SampleSalesTable, product => product.Id, sale => sale.ProductId,
        (product, sales) => new {product, sales})
    .SelectMany(s => s.sales.DefaultIfEmpty(), (s, subSales) => new {s, subSales})
    .GroupBy(ss => new {ss.s.product.Id, ss.s.product.TotalSales}, ss => ss.subSales)
    .Select(grp => new {grp.Key.Id, TotalSales = grp.Sum(s => s.Amount) + grp.Key.TotalSales})).ToList();

如果出于某种原因迫切希望使用方法语法,那么等效的LINQ查询也将起作用:

{{1}}