Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correlated subquery (N+1 fix) missing ORDER BY clause inside nested query #11163

Closed
simeyla opened this issue Mar 6, 2018 · 3 comments
Closed
Assignees
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. type-bug
Milestone

Comments

@simeyla
Copy link

simeyla commented Mar 6, 2018

I am trying to do a simple query to get a list of Club rows with associated customer Order rows. (This is for a subscription model where a customer has a Club membership).

With the new EFCore 2.1 functionality only two queries are generated to avoid N+1 issues. I get back the correct results, until I add OrderByDescending in which case no data is returned for the 'child' query.

It is clear what is happening by looking at the generated SQL. The first query here gets the top @_p_0 clubs, and the second query gets the corresponding orders.

  SELECT TOP(@__p_0) [c].[ClubId] AS [ClubId], [c].[NextShipmentDt]
  FROM [Club] AS [c]
  ORDER BY [c].[NextShipmentDt] DESC, [c].[ClubId]


  SELECT [t].[NextShipmentDt], [t].[ClubId], 
		 [c.OrderNavigation].[CompletedOrderId] AS [OrderID], 
		 [c.OrderNavigation].[OrderDt] AS [OrderDate], 
		 [c.OrderNavigation].[ClubId]
  FROM [Order] AS [c.OrderNavigation]
  INNER JOIN (
      SELECT TOP(@__p_0) [c0].[NextShipmentDt], [c0].[ClubId]
      FROM [Club] AS [c0]
  ) AS [t] ON [c.OrderNavigation].[ClubId] = [t].[ClubId]
  ORDER BY [t].[NextShipmentDt] DESC, [t].[ClubId]

This is great SQL except for one thing! The ORDER BY [t].[NextShipmentDt] DESC should be INSIDE the INNER query because it uses SELECT TOP. This results in completely the wrong orders being returned for the club memberships. Then EFCore fails to match them up and returns me zero results.

Expected correct SQL (one line added)

  SELECT [t].[NextShipmentDt], [t].[ClubId], 
		 [c.OrderNavigation].[CompletedOrderId] AS [OrderID], 
		 [c.OrderNavigation].[OrderDt] AS [OrderDate], 
		 [c.OrderNavigation].[ClubId]
  FROM [Order] AS [c.OrderNavigation]
  INNER JOIN (
      SELECT TOP(@__p_0) [c0].[NextShipmentDt], [c0].[ClubId]
      FROM [Club] AS [c0]
      ORDER BY [t].[NextShipmentDt] DESC
  ) AS [t] ON [c.OrderNavigation].[ClubId] = [t].[ClubId]
  ORDER BY [t].[NextShipmentDt] DESC, [t].[ClubId]

Steps to reproduce

The code is very simple, uses ToList() to force the new behavior as described in the release notes. I was able to repeat this result with any query containing OrderByDescending.

var clubsAndOrders = _context.Clubs
                    .Select(c => new
                    {
                        ClubID = c.ID,
                        NextShipment = c.NextShipmentDt,

                        Orders = c.OrderNavigation.Select(o => new
                        {
                            OrderID = o.OrderId,
                            ShippingAddress = o.Address

                        }).ToList()
                    })
                    .OrderByDescending(x => x.NextShipmentDt)
                    .Take(20)
                    .ToList();

(This code differs slightly from the code to generate the SQL, but the structure is the same).

OrderNavigation is defined in the Club entity as:

    public ICollection<Order> OrderNavigation { get; set; }

with the following constraint:

            entity.HasOne(d => d.Club)
                .WithMany(p => p.OrderNavigation)
                .HasForeignKey(d => d.ClubId)
                .HasConstraintName("FK_Order_Club");

Further technical details

EF Core version: Entity Framework Core 2.1.0-preview1-28290
Database Provider: Microsoft.EntityFrameworkCore.SqlServer
Operating system: Win 10
IDE: Visual Studio 2017 15.6.0 Preview 7

@ajcvickers ajcvickers added this to the 2.1.0 milestone Mar 7, 2018
@maumar
Copy link
Contributor

maumar commented Mar 7, 2018

this is already fixed in d787c13

@maumar maumar closed this as completed Mar 7, 2018
@maumar maumar added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Mar 7, 2018
@maumar
Copy link
Contributor

maumar commented Mar 7, 2018

we produce the following SQL now:

SELECT TOP(@__p_0) [c].[ID] AS [ClubID], [c].[NextShipmentDt] AS [NextShipment]
FROM [Clubs] AS [c]
ORDER BY [NextShipment] DESC, [c].[ID]

SELECT [t].[NextShipmentDt], [t].[ID], [c.OrderNavigation].[OrderId] AS [OrderID], [c.OrderNavigation].[Address] AS [ShippingAddress], [c.OrderNavigation].[ClubID]
FROM [Orders] AS [c.OrderNavigation]
INNER JOIN (
	SELECT TOP(@__p_0) [c0].[NextShipmentDt], [c0].[ID]
	FROM [Clubs] AS [c0]
	ORDER BY [c0].[NextShipmentDt] DESC, [c0].[ID]
) AS [t] ON [c.OrderNavigation].[ClubID] = [t].[ID]
ORDER BY [t].[NextShipmentDt] DESC, [t].[ID]

@simeyla
Copy link
Author

simeyla commented Mar 8, 2018

@maumar Thanks for taking the time to provide the code. Tried to search for this issue because I figured it was more than likely already in the pipeline, but wanted to make sure :-)

@ajcvickers ajcvickers modified the milestones: 2.1.0-preview2, 2.1.0 Nov 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. type-bug
Projects
None yet
Development

No branches or pull requests

4 participants