在下面的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 |
+-------+------------+
答案 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
语句,然后似乎需要根据Sales
对ProductId
表进行分组:
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语句,所以不需要在选择区域中使用条件。
您可以在以下链接中看到所需的结果:
答案 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)
答案 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}}