Skip to content

Remove OrderByClause from query models with Contains, All and Any result operators #2510

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

Merged
merged 7 commits into from
Sep 4, 2020

Conversation

hazzik
Copy link
Member

@hazzik hazzik commented Sep 2, 2020

Improvement for an unwanted behavior found on #2480

@hazzik
Copy link
Member Author

hazzik commented Sep 2, 2020

/cc @ngbrown

@bahusoid
Copy link
Member

bahusoid commented Sep 2, 2020

You can't simply remove OrderBy as it does matter for sub-queries with TOP:

var ordersQuery = db.Orders
                    .Where(x => x.Employee.EmployeeId > 5)
                    .OrderBy(x => x.OrderId)
		    .Take(2);

var orderLinesFuture = db.OrderLines
                         .Where(x => ordersQuery.Any(o => o == x.Order))
                         .OrderBy(x => x.Id)
                         .ToFuture();

In case paging rewriter this OrdereBy and TOP goes to IN subquery so it's OK to remove it in outer query for exists and in:

select
*
from
    OrderLines orderline0_ 
where
    exists (
        select
            order1_.OrderId 
        from
            Orders order1_ 
        where
            order1_.OrderId=orderline0_.OrderId 
            and (
                order1_.OrderId in (
                    select
                        TOP (@p0) order2_.OrderId 
                    from
                        Orders order2_ 
                    where
                        order2_.EmployeeId>@p1 
                    order by
                        order2_.OrderId asc -- this order by CAN'T be removed
                )
            --order by order2_.OrderId asc -- this can be removed
            )
        ) 
    order by
        orderline0_.OrderLineId asc;

@hazzik

This comment has been minimized.

@bahusoid

This comment has been minimized.

@hazzik
Copy link
Member Author

hazzik commented Sep 2, 2020

Yeah, it seems it runs a second time again. And removes after PagingRewriter....

@hazzik hazzik marked this pull request as draft September 2, 2020 09:08
@hazzik hazzik marked this pull request as ready for review September 2, 2020 09:16
@hazzik
Copy link
Member Author

hazzik commented Sep 2, 2020

@bahusoid ok. Seems to be fixed.

@bahusoid
Copy link
Member

bahusoid commented Sep 2, 2020

Now it seems we are doing twice job that should be done only once by your code. How safe would it be to move RemoveUnnecessaryBodyOperators right after PagingRewriter? Then it seems we could drop check introduced in 848b915:

!(queryModel.ResultOperators.Count == 1 && queryModel.ResultOperators.All(x => x is AnyResultOperator || x is ContainsResultOperator || x is AllResultOperator)))

@bahusoid
Copy link
Member

bahusoid commented Sep 2, 2020

How safe would it be

Not quite... CanExecuteCountWithOrderByArguments fails...

@hazzik
Copy link
Member Author

hazzik commented Sep 2, 2020

This thing would not pick up the QueryModel which is processed by PagingRewriter. Or at least it should not.

@bahusoid
Copy link
Member

bahusoid commented Sep 2, 2020

It' just got me thinking that #2480 doesn't fix subqueries with COUNT operator:

[Test(Description = "GH2479")]
public void OrdersWithSubquery9Count()
{
	if (Dialect is MySQLDialect)
		Assert.Ignore("MySQL does not support LIMIT in subqueries.");

	var ordersQuery = db.Orders
	                    .Where(x => x.Employee.EmployeeId > 5)
	                    .OrderByDescending(x => x.OrderId)
	                    .Take(2);

	var orderLinesFuture = db.OrderLines
							.Where(x => ordersQuery.Count(o => o == x.Order) == 2)
							.OrderBy(x => x.Id)
							.ToFuture();

	var orderLines = orderLinesFuture.ToList();
}

Though for some reasons I don't see Count result operator in generated subquery.

@bahusoid
Copy link
Member

bahusoid commented Sep 2, 2020

This thing would not pick up the QueryModel which is processed by PagingRewriter. Or at least it should not.

Not sure what you meant but calling RemoveUnnecessaryBodyOperators right after PagingRewriter does remove ORDER BY without changes applied in 848b915

So in theory we can just call it right from PagingRewriter when we detect that it might be useful instead of copy/paste checks. But for some reasons COUNT for subquery is not present in ResultOperators so it won't fix Count queries

@hazzik
Copy link
Member Author

hazzik commented Sep 2, 2020

Though for some reasons I don't see Count result operator in generated subquery.

It is rewrited to NhCountExpression earlier.

@hazzik
Copy link
Member Author

hazzik commented Sep 2, 2020

calling RemoveUnnecessaryBodyOperators right after PagingRewriter

But RemoveUnnecessaryBodyOperators needs to be called earlier because some other rewriters could be dependent on the removed queries.

@bahusoid
Copy link
Member

bahusoid commented Sep 2, 2020

But RemoveUnnecessaryBodyOperators needs to be called earlier

I meant calling it second time only when query is converted to Contains subquery. But maybe not as it's anyway doesn't handle Count

@hazzik
Copy link
Member Author

hazzik commented Sep 2, 2020

@bahusoid how about that implementation?

Let's deal with count in a separate request.

@bahusoid
Copy link
Member

bahusoid commented Sep 2, 2020

how about that implementation?

I like it

@hazzik hazzik changed the title Remove OrderByClause from queries with Contains, All and Any result operators Remove OrderByClause from query models with Contains, All and Any result operators Sep 4, 2020
@hazzik hazzik merged commit 70c79ee into nhibernate:master Sep 4, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants