代码之家  ›  专栏  ›  技术社区  ›  Saurabh Kumar

Linq认为我需要一个额外的内部连接,但是为什么呢?

  •  2
  • Saurabh Kumar  · 技术社区  · 16 年前

    我有一个LINQ查询,出于某种原因,它正在生成一个额外的/重复的内部联接。这导致查询未返回预期的输出。如果我手动注释从生成的SQL中得到的额外连接,那么我得到的输出看起来是正确的。

    你能知道我在这个LINQ中做了什么导致了这个额外的连接吗?

    谢谢。

    这是我的大约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 回复  |  直到 15 年前
        1
  •  0
  •   jwendl    15 年前

    它们不会被复制。您要求从数据源获取两个不同的值。

    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]   
    

    至于内部联接,获得它们的原因是因为这些表之间的关系。例如,Person是1..1和PersonPhoneNumber(或1..*)。在这两种情况下,我假设PersonPhoneNumber上的PersonID是fk和pk值。因此,在这种情况下,数据源必须转到该外部表,以查看PersonPhoneNumber导航属性的值是否实际存在。它通过在该表上执行内部联接来实现这一点。

        2
  •  0
  •   DigDoug    16 年前

    我的直觉是.distinct().count()由linq-to-sql翻译单独处理。

    我敢打赌,SQL上的执行计划只是把dupe抛出了。

        3
  •  0
  •   ZXX    15 年前

    尝试用显式条件重写而不是抽象的“谓词”构造。从我在SQL中看到的情况来看,对于一个单独的解析器来说,这种组合可能看起来很奇怪,并且有一个join[t5],您刚才称之为dupe:-)可以满足这个条件。

    另外,试着告诉我们您真正想用这个查询找到什么,并试着编写正常的SQL来满足您的需要。我本该是人的——)我也觉得很奇怪——)

    从技术上讲,通过在两个单独的查询中使用条件on(每个var赋值在技术上都是单独的查询)来强制双重联接。

    同样,在不进行任何聚合的情况下按列分组并不总是等同于选择distinct。尤其是,允许联接上的select distinct优先于联接-查询是取消排序的(可以进行重新排序),并且您试图强制它是过程性的。所以Linq给了您确切的过程:-)然后SQL根据SQL规则重新排序:-)

    所以,只需先编写普通的SQL,如果您不能对其进行Linq化,就可以将其放入存储过程中,这样可以使它更快地执行:—)