C# LINQ外部联接具有重复项

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

我正在LINQPad中运行此查询。除了ProductSeries具有重复记录外,它可以正常工作

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());