C# LINQ外部联接具有重复项
我正在LINQPad中运行此查询。除了ProductSeries具有重复记录外,它可以正常工作C# LINQ外部联接具有重复项,c#,linq,linq-to-sql,linqpad,C#,Linq,Linq To Sql,Linqpad,我正在LINQPad中运行此查询。除了ProductSeries具有重复记录外,它可以正常工作 var query = from etaRecord in EtaRecord_0140 join productSeriesRecord in ProductSeries on etaRecord.ProductSeriesID equals productSeriesRecord.ProductSeriesID into productSeriesGroup from produ
var query = from etaRecord in EtaRecord_0140
join productSeriesRecord in ProductSeries
on etaRecord.ProductSeriesID equals productSeriesRecord.ProductSeriesID
into productSeriesGroup
from productSeries in productSeriesGroup.DefaultIfEmpty()
where etaRecord.State == "A"
select new { EtaRecord = etaRecord, ProductSeriesRecord = productSeries };
query.Dump();
我尝试使用FirstOrDefault()
而不是DefaultIfEmpty()
,但出现以下错误:
类型为“LINQPad.User.ProductSeries”的表达式不允许在
源类型为的查询表达式中的后续from子句
“System.Linq.IQueryable”。中的类型推断失败
调用“SelectMany”
如何获取ProductSeries的FirstOrDefault()
,以便每个eRecord只有一行
.NET fiddle在这里:您是说您加入的收藏有重复的吗?因为如果是这样,您可以提前对ProductSeries集合进行分组
var query = from etaRecord in EtaRecord_0140
join productSeriesRecord in ProductSeries.GroupBy(series => series.ProductSeriesID).Select(seriesGroup => seriesGroup.First())
on etaRecord.ProductSeriesID equals productSeriesRecord.ProductSeriesID
into productSeriesGroup
from productSeries in productSeriesGroup.DefaultIfEmpty()
where etaRecord.State == "A"
select new { EtaRecord = etaRecord, ProductSeriesRecord = etaRecord };
query.Dump();
现在假设您使用的是静态列表,而不是数据库。如果它是一个DB连接,那么您可能应该对结果执行distinct操作。事后也可以用类似的方式完成。看起来您需要分组:
var query = from etaRecord in EtaRecord_0140
join productSeriesRecord in ProductSeries
on etaRecord.ProductSeriesID equals productSeriesRecord.ProductSeriesID
into productSeriesGroup
from productSeries in productSeriesGroup.DefaultIfEmpty()
where etaRecord.State == "A"
group productSeries by new { etaRecord.ProductSeriesId, etaRecord } into g
select new
{
EtaRecord = g.Key.etaRecord,
ProductSeriesRecord = g.Select(x => x).FirstOrDefault()
};
我会在子查询中执行此操作:
var query = from etaRecord in EtaRecord_0140
where etaRecord.State == "A"
select new
{
EtaRecord = etaRecord,
ProductSeriesRecord =
(from productSeriesRecord in ProductSeries
where productSeriesRecord.ProductSeriesID == etaRecord.ProductSeriesID
select productSeriesRecord).FirstOrDefault()
};
在LINQ to对象中,这可能是一个低效的操作,因为子查询是针对每个etaRecord
执行的,但由于整个语句都转换为SQL,查询优化器将负责优化的执行计划
这就是LINQ to entities的故事
LINQtoSQL似乎总是为组联接(即join-into
)与FirstOrDefault()结合生成n+1查询。我已经尝试了几个场景,但我无法让它只生成一个查询。我能找到的生成一个查询的唯一解决方案是:
var query = from etaRecord in EtaRecord_0140
where etaRecord.State == "A"
from productSeriesRecord in
ProductSeries
.Where(ps => ps.ProductSeriesID == etaRecord.ProductSeriesID)
.Take(1)
.DefaultIfEmpt()
select new { EtaRecord = etaRecord, ProductSeriesRecord = productSeries };
因此,联接语法被放弃,并以一种相当人为的方式查询属于EtaRecord
的ProductSeries
的第一条记录。问题在于您的额外from
子句:
from productSeries in productSeriesGroup.DefaultIfEmpty()
你应该抛弃它,只需使用:
let productSeries = productSeriesGroup.FirstOrDefault()
。。。或者只需在select
子句中使用productSeriesGroup.FirstOrDefault()
,如下所示:
var query = from etaRecord in etaRecords
join productSeriesRecord in productSeriesRecords
on etaRecord.ProductSeriesId equals productSeriesRecord.ProductSeriesId
into productSeriesGroup
select new { EtaRecord = etaRecord,
ProductSeriesRecord = productSeriesGroup.FirstOrDefault() };
无论是哪种更改,现在的结果都是:
Snuh 1 - null
Snuh 2 - null
Snuh 3 - null
Snuh 4 - Description A
Snuh 5 - null
Snuh 6 - Description B
我想这正是您想要的。您应该能够向group by EtaRecord添加额外的筛选步骤,并且只需选择每个组的第一条记录
即
left outer join可以返回重复项,显示您期望的内容和当前输出。查询当前不包含重复项,因此我无法显示错误的输出。问题是数据可能会发生变化,然后在将来会出现重复。我需要警惕这一点。我只想要一行一个etaRecord。而且etaRecord可以有一个空的ProductSeries,或者只有一个ProductSeries,即使有多个ProductSeries。在我的帖子中添加了.NET fiddle链接。你能解释一下你对示例代码的预期结果吗?@BobHorn update fiddle:这正是我要说的。我刚刚尝试了您的查询,但现在所有行都有一个空ProductSeriesRecord.Ah,如果您删除DefaultIfEmpty?听起来DefaultIfEmpty正在返回类型的默认值,而引用类型的默认值为Nothing/Null。如果我删除DefaultIfEmpty()
,那么我只会得到包含ProductSeries的行。我丢失了所有没有的行。在您的组行中,我必须将productSeriesGroup
更改为productSeries
。现在我只剩下一个错误:名称“productSeriesRecord”在当前上下文中不存在
在组行:名称“productSeriesRecord”在当前上下文中不存在
我想这已经接近了。只返回了20行,这是不对的。只有一条记录为null,另外19条记录有产品系列。应该有许多行的product series为空。在最后一行:“System.Linq.igroupping”不包含“etaRecord”的定义,并且没有扩展方法“etaRecord”接受productSeries相同问题类型的第一个参数。@EhsanSajjad不确定这是否适用于此处。我只是添加了一些关于性能的注释。这似乎返回了正确的结果,但生成了数千条SQL语句,这正是我最初试图解决的问题。执行该操作需要40秒,而不是1秒。最初查询中包含let
,但它导致生成数千条SQL语句。让我试试你的密码。谢谢。有三个问题:所有的产品系列都是空的,运行了47秒(我的帖子中的查询大约需要1秒),并且生成了数千条SQL语句。我想知道,如果只获取副本然后删除它们是否会更好/更容易?@BobHorn:那么听起来你的加入可能无法正常工作。(使用LINQ to对象,您的示例也可以)。如果只使用简单的内部联接,会发生什么?我会先处理正确性,然后计算效率。请注意,FirstOrDefault()
可能会由于缺少顺序而导致问题。。。您可以尝试在productSeriesRecords
上指定一个明确的顺序,看看是否有帮助。正如我所说的,我首先要弄清楚正确性。通过添加另一个where
子句,我可以让自己的查询正常工作。您的查询和我的新查询现在返回相同数量的行。不幸的是,我的新查询不是这个问题的通用解决方案。因为我现在知道重复项在其中一列中有一个特定的值,所以我可以添加以下内容:where productSeries==null | | | productSeries.lamplate==null | | productSeries.lamplate!=“Unknown”
@BobHorn:我们仍然无法解释我的查询为什么不起作用——据我所知,应该是完全没有问题的。如果您将select
更改为select new{EtaRecord=EtaRecord,Count=productSeriesGroup.Count()}
您得到了什么?不幸的是,虽然我们无法重现这个问题,但很难帮助我们。
query = (from r in query
group r by r.EtaRecord.EtaId into results
select results.FirstOrDefault());