Why does EF add tons of unused OUTER APPLY clauses when I put OrderBy at the beginning of my query

冷暖自知 提交于 2019-12-23 18:03:48

问题


tl;dr; Putting orderby contact.Property before my projection (let defaultAddress = contact.Addresses...) creates an exponentially more complex SQL statement and sometimes causes an OutOfMemoryException.

Question

I have a simple data model for storing contact information

public class Contact {
    public int ContactID { get; set; }

    public virtual ICollection<Address> Addresses { get; set; }
    //etc
}

public class Address {
    public int AddressID { get; set; }

    public int ContactID { get; set; }
    public virtual Contact Contact { get; set; }
    //etc
}

I want to project that into a flat model and sort the results, for example

from c in Contacts
let a = c.Addresses.FirstOrDefault()
orderby c.DOB
select new {
    ContactID = c.ContactID,
    AddressID = a.AddressID
}

That generates straightforward SQL

SELECT 
    [Project2].[ContactID] AS [ContactID], 
    [Project2].[C1] AS [C1]
    FROM ( SELECT 
        [Extent1].[ContactID] AS [ContactID], 
        [Extent1].[DOB] AS [DOB], 
        (SELECT TOP (1) 
            [Extent2].[AddressID] AS [AddressID]
            FROM [dbo].[Addresses] AS [Extent2]
            WHERE [Extent1].[ContactID] = [Extent2].[ContactID]) AS [C1]
        FROM [dbo].[Contacts] AS [Extent1]
    )  AS [Project2]
    ORDER BY [Project2].[DOB] ASC

If I switch the order of the orderby c.DOB to be before let a = ..., EF adds an entire extra OUTER APPLY statement to the generated SQL.

SELECT 
    [Project2].[ContactID] AS [ContactID], 
    [Project2].[C1] AS [C1]
    FROM ( SELECT 
        [Extent1].[ContactID] AS [ContactID], 
        [Extent1].[DOB] AS [DOB], 
        (SELECT TOP (1) 
            [Extent3].[AddressID] AS [AddressID]
            FROM [dbo].[Addresses] AS [Extent3]
            WHERE [Extent1].[ContactID] = [Extent3].[ContactID]) AS [C1]
        FROM  [dbo].[Contacts] AS [Extent1]
        OUTER APPLY  (SELECT TOP (1) [Extent2].[AddressID] AS [AddressID]
            FROM [dbo].[Addresses] AS [Extent2]
            WHERE [Extent1].[ContactID] = [Extent2].[ContactID] ) AS [Limit1]
    )  AS [Project2]
    ORDER BY [Project2].[DOB] ASC

The query gets significantly more complicated and has exponentially more unused OUTER APPLYs, LEFT JOINs and CROSS JOINs if I add more collection properties to my projection or if I add a filter to the collections. If the query is has enough flat properties putting the orderby in the wrong place will cause an OutOfMemoryException while trying to build a query plan for the massive SQL that gets generated!


回答1:


It requires intimate knowledge of EF's source code to understand how the SQL generator parses an expression into SQL. It's not my ambition to dig that deep (that would take many days), so unless someone from the EF team chimes in, I guess we have to live with the facts as they are.

It's always good to know about issues like this. Having seen more issues at Stack Overflow, it seems a rule of the thumb emerges:

Sort as late as possible in a LINQ statement

Another case I already knew of is a query like Set<T>().OrderBy(...).GroupBy(...). Here, the OrderBy doesn't cause a bad query shape. It's ignored completely! Conversely, the statement Set<T>().GroupBy(...).OrderBy(...) does translate into a SQL ORDER BY, but of course the statements are semantically different.



来源:https://stackoverflow.com/questions/46151694/why-does-ef-add-tons-of-unused-outer-apply-clauses-when-i-put-orderby-at-the-beg

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!