Paging is incorrect for Oracle dialects when the rows have been ordered

Description

Given a snippet such as:

NHibernate is generating SQL like this for the Oracle 10 dialect (all the way down to 8):

The problem with the query generated by NHibernate is that both where-clause checks should be on "rownum_" and outside the final closing parenthesis. As-written, NHibernate constructs a query telling Oracle to throw away the ordering before running the "rownum_ > 500" check => some page results will miss rows they should include while including results from another page query they should not.

I wasn't the first to be confused by this - see http://stackoverflow.com/questions/13292211/oracle-10g-paging-returns-wrong-resultset-with-rownum-and-nhibernate?rq=1 and ignore the fact he fat-fingered his example.

The fix is pretty simple: line 290 of Oracle8iDialect.cs (https://github.com/nhibernate/nhibernate-core/blob/master/src/NHibernate/Dialect/Oracle8iDialect.cs) needs to be changed to:

The attached file includes the fix and is based on your official sources for 4.0.3, not the master on Github.

Thanks!

Environment

64-bit Windows 7, .NET 4.5, 32-bit CLR, ASP.NET MVC 5

Attachments

1

Activity

Show:

Alex Zaytsev August 7, 2015 at 5:18 AM

Ok. The real problem is that your order column is not really stable (or oracle sorting is unstable). It does not maintain the same sort order for different queries. However for some reasons moving this condition outside solves the problem. The queries has slightly different execution plans, which different in the details.

So, I'll reopen this issue, but as improvement with lower priority. So, for the work-around I would like to suggest to add additional column to make sorting more stable (in the case of sql fiddle example: "ID", http://sqlfiddle.com/#!4/f066c/5)

Dave Fugate August 7, 2015 at 4:52 AM
Edited

Some issue with sqlfiddle is stopping me from including the correct SQL NHibernate should be generating for the test case above. Here it is:

Dave Fugate August 7, 2015 at 4:37 AM

Please reopen this

Here's the failing test case - use the following to create the table for your SQL fiddle:
CREATE TABLE A (ID INT, D VARCHAR(10));

INSERT INTO A VALUES (1, 'A');
INSERT INTO A VALUES (2, 'A');
INSERT INTO A VALUES (3, 'A');
INSERT INTO A VALUES (4, 'A');
INSERT INTO A VALUES (5, 'A');
INSERT INTO A VALUES (6, 'A');
INSERT INTO A VALUES (7, 'A');
INSERT INTO A VALUES (8, 'B');
INSERT INTO A VALUES (9, 'B');
INSERT INTO A VALUES (10, 'B');
INSERT INTO A VALUES (11, 'B');
INSERT INTO A VALUES (12, 'B');
INSERT INTO A VALUES (13, 'B');
INSERT INTO A VALUES (14, 'C');
INSERT INTO A VALUES (15, 'C');
INSERT INTO A VALUES (16, 'C');
INSERT INTO A VALUES (17, 'C');
INSERT INTO A VALUES (18, 'C');
INSERT INTO A VALUES (19, 'C');
INSERT INTO A VALUES (20, 'A');

Notice that ID==5 is repeated twice and ID==9 is missing altogether.

Alex Zaytsev August 7, 2015 at 4:19 AM

Please check following SQL fiddle to check that paging is correct http://sqlfiddle.com/#!4/4e416/12

Alex Zaytsev August 7, 2015 at 3:58 AM
Edited

Everything works as expected.

Please read this SO answer http://stackoverflow.com/a/241643/259946, as long as mentioned links (http://weblogs.asp.net/fbouma/api-s-and-production-code-shouldn-t-be-designed-by-scientists) and comments. Especially this one:

I wondered why two WHERE couldn't be combined with AND, and then found this: http://orafaq.com/wiki/ROWNUM – Mengdi Gao Dec 17 '12 at 6:09

So, here is what really happens:

1. Select ordered data

2. Wrap data into subselect (because rownum is applied before ordering)
3. Apply rownum <= 1000
3. Give rownum to the outside of query as rownum_ (because you can not do rownum > 500 at this level)
4. Apply rownum_ > 500

So, if you want to proof that I'm wrong - please contribute a failing test case.

Details

Assignee

Reporter

Labels

Affects versions

Priority

Who's Looking?

Open Who's Looking?

Created August 4, 2015 at 9:17 PM
Updated August 7, 2015 at 5:19 AM
Who's Looking?