我有一个LINQ查询,由于某种原因,它会生成一个额外/重复的INNER JOIN。这导致查询不返回预期的输出。如果我从生成的SQL中手动注释了额外的JOIN,那么我的输出看起来是正确的。
你能否发现我在这个LINQ中做了什么导致了这个额外的JOIN?
感谢。
这是我的近似LINQ
predicate=predicate.And(condition1);
predicate1=predicate1.And(condition2);
predicate1=predicate1.And(condition3);
predicate2=predicate2.Or(predicate1);
predicate=predicate.And(predicate2);
var ids = context.Code.Where(predicate);
var rs = from r in ids
group r by r.PersonID into g
let matchcount=g.Select(p => p.phonenumbers.PhoneNum).Distinct().Count()
where matchcount ==2
select new
{
personid = g.Key
};
这里是生成的SQL(重复连接是[t7])
Declare @p1 VarChar(10)='Home'
Declare @p2 VarChar(10)='111'
Declare @p3 VarChar(10)='Office'
Declare @p4 VarChar(10)='222'
Declare @p5 int=2
SELECT [t9].[PersonID] AS [pid]
FROM (
SELECT [t3].[PersonID], (
SELECT COUNT(*)
FROM (
SELECT DISTINCT [t7].[PhoneValue]
FROM [dbo].[Person] AS [t4]
INNER JOIN [dbo].[PersonPhoneNumber] AS [t5] ON [t5].[PersonID] = [t4].[PersonID]
INNER JOIN [dbo].[CodeMaster] AS [t6] ON [t6].[Code] = [t5].[PhoneType]
INNER JOIN [dbo].[PersonPhoneNumber] AS [t7] ON [t7].[PersonID] = [t4].[PersonID]
WHERE ([t3].[PersonID] = [t4].[PersonID]) AND ([t6].[Enumeration] = @p0) AND ((([t6].[CodeDescription] = @p1) AND ([t5].[PhoneValue] = @p2)) OR (([t6].[CodeDescription] = @p3) AND ([t5].[PhoneValue] = @p4)))
) AS [t8]
) AS [value]
FROM (
SELECT [t0].[PersonID]
FROM [dbo].[Person] AS [t0]
INNER JOIN [dbo].[PersonPhoneNumber] AS [t1] ON [t1].[PersonID] = [t0].[PersonID]
INNER JOIN [dbo].[CodeMaster] AS [t2] ON [t2].[Code] = [t1].[PhoneType]
WHERE ([t2].[Enumeration] = @p0) AND ((([t2].[CodeDescription] = @p1) AND ([t1].[PhoneValue] = @p2)) OR (([t2].[CodeDescription] = @p3) AND ([t1].[PhoneValue] = @p4)))
GROUP BY [t0].[PersonID]
) AS [t3]
) AS [t9]
WHERE [t9].[value] = @p5
答案 0 :(得分:0)
我的直觉是.DISTINCT()。COUNT()由linq到sql的翻译分开处理。
我也打赌,SQL上的执行计划只是抛弃了欺骗。
答案 1 :(得分:0)
尝试用显式条件重写而不是抽象的“谓词”构造。从我在SQL中看到,组合可能看起来很奇怪到一个孤立的解析器和一个你刚刚调用dupe的连接[t5] :-)就是为那个条件提供服务。
另外,尝试告诉我们您真正希望使用该查询找到什么样的山雀,并尝试编写符合您需要的常规SQL。我应该是人类:-)而且我看起来也很奇怪: - ))
从技术上讲,你通过在两个单独的查询中使用条件强制双关节(每个var赋值在技术上分开查询)。
同样按列进行分组而不进行任何聚合并不等同于选择distinct。特别是在连接上选择distinct可以优先于连接 - 查询是声明性的(可以进行重新排序),并且您试图强制它是程序性的。所以LINQ给了你精确的程序:-)然后根据SQL规则重新排序SQL :-))
所以,首先编写普通的SQL,如果你不能LINQ-ize将它放入sproc中 - 无论如何都会让它更快: - )
答案 2 :(得分:0)
它们没有重复。您要求数据源中有两个不同的值。
let matchcount=g.Select(p => p.phonenumbers.PhoneNum).Distinct().Count()
导致
SELECT COUNT(*)
FROM (
SELECT DISTINCT [t7].[PhoneValue]
FROM [dbo].[Person] AS [t4]
INNER JOIN [dbo].[PersonPhoneNumber] AS [t5] ON [t5].[PersonID] = [t4].[PersonID]
INNER JOIN [dbo].[CodeMaster] AS [t6] ON [t6].[Code] = [t5].[PhoneType]
INNER JOIN [dbo].[PersonPhoneNumber] AS [t7] ON [t7].[PersonID] = [t4].[PersonID]
WHERE ([t3].[PersonID] = [t4].[PersonID]) AND ([t6].[Enumeration] = @p0) AND ((([t6].[CodeDescription] = @p1) AND ([t5].[PhoneValue] = @p2)) OR (([t6].[CodeDescription] = @p3) AND ([t5].[PhoneValue] = @p4)))
) AS [t8]
和
from r in ids
group r by r.PersonID into g
导致
SELECT [t0].[PersonID]
FROM [dbo].[Person] AS [t0]
INNER JOIN [dbo].[PersonPhoneNumber] AS [t1] ON [t1].[PersonID] = [t0].[PersonID]
INNER JOIN [dbo].[CodeMaster] AS [t2] ON [t2].[Code] = [t1].[PhoneType]
WHERE ([t2].[Enumeration] = @p0) AND ((([t2].[CodeDescription] = @p1) AND ([t1].[PhoneValue] = @p2)) OR (([t2].[CodeDescription] = @p3) AND ([t1].[PhoneValue] = @p4)))
GROUP BY [t0].[PersonID]
) AS [t3]
对于INNER JOINS,你得到它们的原因是因为这些表之间的关系。例如,Person是带有PersonPhoneNumber(或1 .. *)的1..1。在任何一种情况下,我假设PersonPhoneNumber上的PersonID是FK和PK值。因此,在这种情况下,数据源必须转到该外部表以查看PersonPhoneNumber导航属性的值是否确实存在。它通过在该表上执行INNER JOIN来实现此目的。