Matteo Saporiti
Matteo Saporiti

Reputation: 164

LINQ, Skip and Take ordering

I was doing some testing with Take and Skip and I found that:

var objects = (from c in GetObjects() orderby c.Name select c);
var skipTake = objects.Skip(5).Take(10).ToList();
var takeSkip = objects.Take(10).Skip(5).ToList();

GetObjects() returns an IQueryable generated by NHibernate (3.3.3GA, using a SQL Server 2008 R2 DB).
skipTake and takeSkip contain the same exact sequence of 10 objects.
But if I write

var objects = (from c in GetObjects() orderby c.Name select c).ToList();
var skipTake = objects.Skip(5).Take(10).ToList();
var takeSkip = objects.Take(10).Skip(5).ToList();

skipTake contains the same sequence as the above example while takeSkip contains a different sequence of just 5 objects.

Do Take and Skip calls get reordered when they are applied on a IQueryable?
I would love to get some insight on this.

Thanks in advance.

Upvotes: 7

Views: 4831

Answers (3)

Jaguar
Jaguar

Reputation: 5958

Firstly I find that @https://stackoverflow.com/users/2617732/rdans answer is irrelevant to your specific example since you do not apply a where clause after the Take.

That said, in example A both skipTake and takeSkip should generate the same SQL where it it would select the first 10 rows after the 5th row, (for SQL server for example that would be with ROW_NUMBER OVER(...) ).

It seems to me that in example B you are trying to perform the Take and Skip not on an IQuerable but on a System.Collections.Generic.List<> (which naturally is not NHibernate territory).

In this case objects has N elements.

skipTake will first Skip 10 elements and from the resulting enumeration (which enumerates over N-10 elements) then Take the first 5 elements.

Given then the results are already ordered the result of skipTakes ToList should be the same with the example A.

takeSkip on the other hand first Takes 10 elements and from the resulting enumeration (which enumerates over 10 elements) Skips the first 5.

So, that's that

Upvotes: 0

Ramanagom
Ramanagom

Reputation: 329

Do Take and Skip calls get reordered when they are applied on a IQueryable? I would love to get some insight on this.

I think the answer to this question should be formulated this way:

skipTake is the result of skipping the first 5 elements of the IQueriable and taking the next 10. So for example in a list of ordered numbers from 1 to 20 skipTake would be the sublist 6 --> 15.

takeSkip is the result of taking the first 10 elements of the IQueriable and then skipping the first 5 elements of the sublist (!). So using the list from the previous example, takeSkip would be the sublist 6 --> 10.

For the first part of your observation (where the skipTake and takeSkip contain the same 10 elements), this should be considered as wrong behaviour (or probably even a bug) in the NHibernate implementation.

Upvotes: 1

rdans
rdans

Reputation: 2157

It looks like this is due to a bug in particular versions of nhibernate:

http://sourceforge.net/p/nhibernate/news/2013/03/nhiberate-333ga-released/

BEWARE: In versions prior to 3.3.3.CR1, the handling of the LINQ Take() method was flawed - no matter where in the query Take() was placed it was always applied as if it had been placed at the end. 3.3.3 fixes this, so that Take() now correctly follows the .Net semantics. That is, in 3.3.3, the following queries might now give different results:

session.Query<Foo>.OrderBy(...).Take(5).Where(...);
session.Query<Foo>.Where(...).OrderBy(...).Take(5);

Starting with 3.3.3, the first query will generate a subquery to correctly apply the row limit before the where-clause.

Upvotes: 4

Related Questions