EF和Linq Except

4
给定这两个EF模型,我该如何使用LINQ选择所有在InteractionSegmentDetail中存在但不在Custom_SegmentsParsed中存在的记录?使用InteractionIDKey字段作为比较器。
 public partial class Custom_SegmentsParsed
    {
        public string InteractionIDKey { get; set; }
        public string SegmentNum { get; set; }
        public System.DateTime ServerTs { get; set; }
        public System.DateTime CUTC { get; set; }
        public Nullable<int> Duration { get; set; }
        public string LocalParty { get; set; }
        public string Queue { get; set; }
        public string EndCode { get; set; }
        public string Details { get; set; }
}
  public partial class InteractionSegmentDetail
    {
        public string InteractionIDKey { get; set; }
        public short SiteID { get; set; }
        public byte SeqNo { get; set; }
        public System.DateTime StartDateTimeUTC { get; set; }
        public int StartDTOffset { get; set; }
        public string ConversationID { get; set; }
        public string SegmentLog { get; set; }
}
4个回答

4
我会使用 WhereAny
isdList.Where(isd => !cspList
                     .Any(csp => isd.InteractionIDKey == csp.InteractionIDKey));

这个很棒!在InteractionSegmentDetail表中对750万条记录和Custom_SegmentParsed表中的0条记录执行时间为3.125秒。 - Wjdavis5
2
但我很好奇随着Custom_SegmentsParsed表的增长,性能会如何逐渐降低。 - Wjdavis5

3
dbContext.InteractionSegmentDetail
         .Where(isd => !dbContext.Custom_SegmentsParsed
                                 .Select(x => x.InteractionIDKey)
                                 .Contains(isd.InteractionIDKey));

这将需要dbContext在每次迭代中获取表中的每个InteractionIDKey。 - Travis J
@Servy - 与提供程序相同并不是问题所在。问题在于查看每个InteractionSegmentDetail中的InteractionIDKey值,以及Custom_SegmentsParsed中的每个值。 - Travis J
1
@TravisJ 如果它是一个 Enumerable 的话,当然可以,但是它不是,它是一个查询提供程序。它不会将这个代码作为 C# 代码执行,而是将整个查询转换成 SQL 代码,最终生成一个内部查询在外部查询中执行的单个 SQL 查询。当然,如果查询来自不同的提供程序,则无法执行此操作,代码将会出错。(它不会像你所建议的那样低效并执行 N+1 次查询,只会抛出异常。) - Servy
2
@TravisJ 因为没有迭代。 没有任何迭代。 整个代码块显示的所有内容都会导致创建一个单一的“Expression”对象,查询提供程序将能够检查它作为一个大块并尝试将其转换为SQL。 它会成功,然后数据库将花费愉快的时间将SQL优化为合理的算法并执行查询。 为什么迭代任何东西,如果它这样做了,为什么会每次重新构建数据集? - Servy
1
@TravisJ 为什么不行呢?难道你认为所有的数据库都是由一年级的程序员编写的吗?它们被优化到极致,以避免做你所说的事情,部分原因是因为很多 SQL 是由不知道如何编写高效查询的人编写的。优化这些查询尽可能地是数据库的责任,我看不出它不能在这里执行一个简单而直接的缓存机制的理由。如果你有其他的理由认为它不会成功,除了“数据库不可能尝试优化这个”,那我想看看。 - Servy
显示剩余4条评论

0

这里有一个使用查询语法执行左连接的选项,它将非常高效:

var results = 
    (from isd in db.InteractionSegmentDetails
     join csp in db.Custom_SegmentsParsed on isd.InteractionIDKey equals csp.InteractionIDKey into possibleCSPs
     from csp in possibleCSPs.DefaultIfEmpty()
     where csp.InteractionIDKey == null //ensure no CSP match was found
     select isd).Distinct().ToArray(); //don't include duplicates (just in case)

-1
在这里我会使用三个查询,因为将它们合并在一起会损害性能。
List<string> customKeys = dbContext.Custom_SegmentsParseds.Select(s => s.InteractionIDKey).ToList();
List<string> interactionKeys = dbContext.InteractionSegmentDetails.Select(s => s.InteractionIDKey).ToList();
IEnumerable<string> overLap = interactionKeys.Except(customKeys);
List<InteractionSegmentDetail> detailList = dbContext.InteractionSegmentDetails.Where(seg => overLap.Contains(seg.InteractionIDKey)).ToList();

2
这个查询不必要地将这两个数据库表中的整个ID集合都拉到内存中,然后在内存中执行集合操作,而不是允许数据库来执行它,最后需要另一个非常昂贵的操作来获取结果。这里发布的每一个其他解决方案都会执行得更好 - Servy
@Servy - 其他解决方案需要相同的事情,除了每次迭代只需一次。数据库内存将需要在每次迭代中使用以存储键列表。 - Travis J
2
@TravisJ 不是的,其他的都不会。只有你的会出现这种情况,因为你在执行操作之前明确地将查询转换为列表。如果你没有这样做,就不会发生这种情况。现在你可以通过删除 ToList 调用并不将查询强制转换为 IEnumerable 来修复这个问题;如果你停止主动阻止查询提供程序在单个查询中执行所有操作,那么它就能够准确实现这一点。 - Servy
你认为数据库不知道如何高效地执行集合减法?你觉得C#应用程序在这方面会比数据库好很多吗?它执行这个操作的速度将比数据库慢得多。加上所有网络流量和拉取数据到应用程序以执行操作的额外成本,将会大大增加操作的执行时间,这还不包括内存成本,这显然是禁止的。 - Servy
@Servy - 集合的减法可能很容易,但是正确构建集合是我担心数据库做出正确决策的地方。这些集合是在减法之前构建的吗?在减法期间构建?每次在添加详细键之前都需要检查整个自定义键集吗?它会转换为哈希表并使用它来进行减法运算吗? - Travis J
显示剩余11条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接