VB.NET LINQ多实体查询如何生成含DISTINCT的SQL语句?
这个问题我之前也碰到过——EF(Entity Framework)有时候确实不会把LINQ里的Distinct()直接转换成SQL的DISTINCT,尤其是当你直接选择整个实体对象组合的时候。不过别担心,有几种简便的方法能让你实现目标:
方法1:使用匿名类型选择字段(推荐)
当你直接选择t, u, v这类实体对象时,EF很难在数据库层面判断两个实体组合是否“重复”(因为实体的相等性判断默认依赖引用相等),所以会先把所有数据拉到内存再执行去重。
改用匿名类型选择你需要的具体字段,EF就能识别出如何在数据库层面做去重,从而生成带DISTINCT的SQL:
Using context = New abcContext() Dim info = From t In context.class1 Join u In context.class2 On t.id Equals u.id Join v In context.class3 On t.id Equals v.id And t.date Equals v.date Select New With { .Class1Id = t.id, .Class1Date = t.date, .Class2Field = u.SomeImportantField, .Class3Field = v.AnotherField } .Distinct() End Using
这段代码生成的SQL会包含SELECT DISTINCT,因为匿名类型的相等性是基于其所有属性值的,EF可以把这个逻辑直接映射到数据库的去重操作。
方法2:用子查询先获取去重的标识,再关联实体
如果你必须返回完整的实体对象(而不是匿名类型),可以先通过子查询在数据库层面获取去重的id和date,再基于这些标识关联三个表:
Using context = New abcContext() ' 先在数据库层面获取去重的id和date组合 Dim distinctKeys = (From t In context.class1 Join u In context.class2 On t.id Equals u.id Join v In context.class3 On t.id Equals v.id And t.date Equals v.date Select New With { t.id, t.date }).Distinct() ' 再通过去重的key关联获取完整实体 Dim info = From key In distinctKeys Join t In context.class1 On key.id Equals t.id And key.date Equals t.date Join u In context.class2 On t.id Equals u.id Join v In context.class3 On t.id Equals v.id And t.date Equals v.date Select t, u, v End Using
第一个子查询会生成带DISTINCT的SQL,后续的关联查询只会基于去重后的key拉取数据,既保证了结果去重,也避免了把大量重复数据拉到内存。
方法3:检查并升级EF版本
如果你使用的是较旧的EF版本(比如EF5及更早),对Distinct()的转换支持确实有限。升级到EF6或者EF Core的新版本后,EF对实体组合的Distinct()转换会更智能,有可能直接把你的原始写法转换成带DISTINCT的SQL。
总结一下:LINQ查询多实体时完全可以生成带DISTINCT的SQL,只是需要调整查询结构让EF能正确识别并推送去重操作到数据库,而不是在内存中处理。
内容的提问来源于stack exchange,提问作者Rolan

