LINQ认为我需要额外的INNER JOIN,但为什么呢?

时间:2010-05-15 11:41:22

标签: sql linq linq-to-sql

我有一个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

3 个答案:

答案 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来实现此目的。