LINQ使用Union合并两表数据时如何按ProductNumber填充缺失字段值
问题原因
你当前写法的问题出在Union的两个分支上:查询Products表时,把订单相关的OrderNumber、Amount字段硬编码为0;查询OrderItems表时,又把产品基础属性Cost、Price、Name、Inventory硬编码为0或空串。Union 本身只负责合并两个结果集、对完全重复的行去重,不会自动匹配相同ProductNumber的记录补全空缺字段,最终结果自然会出现大量字段为默认空值的情况。
推荐实现方案
不要硬编码空值再事后补数,直接以Products表(存储产品固定属性)为主表左连接OrderItems表(存储订单关联数据),一次投影就能生成完整字段的结果,需要手动指定特定产品的字段值时,直接在投影逻辑里加判断即可,所有计算可以直接在数据库侧完成,性能更好。
方法语法版本代码:
var test = db.Products .GroupJoin( db.OrderItems, p => p.ProductNumber, oi => oi.ProductNumber, (p, orderItems) => new { Product = p, OrderItems = orderItems } ) .SelectMany( x => x.OrderItems.DefaultIfEmpty(), (x, oi) => new UnionTable { ProductNumber = x.Product.ProductNumber, OrderNumber = oi?.OrderNumber ?? 0, Cost = x.Product.Cost, // 针对ProductNumber=1的记录单独指定Price和Name,其余产品取表内存储值 Price = x.Product.ProductNumber == 1 ? 10 : x.Product.Price, Name = x.Product.ProductNumber == 1 ? "almond" : x.Product.Name, Amount = oi?.Amount ?? 0, Inventory = x.Product.Inventory } ) .ToList();
查询语法版本代码:
var test = from p in db.Products join oi in db.OrderItems on p.ProductNumber equals oi.ProductNumber into oiGroup from orderItem in oiGroup.DefaultIfEmpty() select new UnionTable { ProductNumber = p.ProductNumber, OrderNumber = orderItem?.OrderNumber ?? 0, Cost = p.Cost, Price = p.ProductNumber == 1 ? 10 : p.Price, Name = p.ProductNumber == 1 ? "almond" : p.Name, Amount = orderItem?.Amount ?? 0, Inventory = p.Inventory };
兼容原有Union写法的补全方案
如果你因为业务逻辑必须保留先Union的结构,可以在Union结果的基础上,按ProductNumber分组,取同组内对应字段的非默认值填充,手动指定的固定值也可以在这一步统一赋值:
// 原有Union查询逻辑不变 var rawData = (from table1 in db.Products select new UnionTable() { ProductNumber = table1.ProductNumber, OrderNumber = 0, Cost = table1.Cost, Price = table1.Price, Name = table1.Name, Amount = 0, Inventory = table1.Inventory }).Union (from table2 in db.OrderItems select new UnionTable() { ProductNumber = table2.ProductNumber, OrderNumber = table2.OrderNumber, Cost = 0, Price = 0, Name = string.Empty, Amount = table2.Amount, Inventory = 0 }); // 二次聚合补全字段 var test = rawData .GroupBy(r => r.ProductNumber) .Select(g => new UnionTable { ProductNumber = g.Key, OrderNumber = g.Max(r => r.OrderNumber), Cost = g.Max(r => r.Cost), Price = g.Key == 1 ? 10 : g.Max(r => r.Price), Name = g.Key == 1 ? "almond" : g.First(r => !string.IsNullOrEmpty(r.Name)).Name, Amount = g.Max(r => r.Amount), Inventory = g.Max(r => r.Inventory) }) .ToList();
注意:优先选第一种Join写法,这种方式不需要拉取大量带硬编码空值的冗余数据,数据库执行效率比先Union再内存聚合高很多,数据量越大优势越明显。
内容的提问来源于stack exchange,提问作者user17712660
相关产品推荐
相关产品推荐

