Reputation: 164
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
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 skipTake
s ToList
should be the same with the example A.
takeSkip
on the other hand first Take
s 10 elements and from the resulting enumeration (which enumerates over 10 elements) Skip
s the first 5.
So, that's that
Upvotes: 0
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
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