2012-11-08 82 views
1

當我在使用Oracle 10g和NHibernate分頁數據時,我們遇到了一些奇怪的行爲。Oracle 10g分頁返回錯誤的結果集'rownum'和NHibernate

我有一個20行的表,我想在列表中顯示每個站點10行,所以我的列表有2個頁面。

使用NHibernate我設置爲第一頁query.SetMaxResults(10).SetFirstResult(0), 和第二頁query.SetMaxResults(10).SetFirstResult(10)

下面的SQL將第一頁創建:

SELECT * FROM table WHERE rownum <= 10; 

下面的SQL將進行第二頁創建:

SELECT row_.*, rownum rownum_ 
FROM 
(
    SELECT * 
    FROM table 
) row_ 
WHERE rownum <= 20 WHERE rownum_ > 10; 

首先查詢正確返回前10行,而第二個查詢返回只有4個新行和6行已經在「頁面1」中。所以6行完全缺失。

所以,我想,這到底是什麼?

+0

你可以發佈完整的查詢嗎?你是否渴望加載任何孩子? – Rippo

+0

你的第二個查詢是使用外部查詢中的'rownum'兩次。根本沒有使用內部查詢。 – Ben

+0

由於在外部查詢中有兩個「WHERE」關鍵字,因此您的查詢看起來會變形。在第1頁和第2頁之間有沒有DML?也theres沒有秩序..因此不保證排序。 – DazzaL

回答

0

也許我失去了一些東西,但我想你想這對於你的第二個查詢:

SELECT row_.* 
FROM 
(
    SELECT *, rownum rn --- place your rownum here 
    FROM table 
) row_ 
WHERE rn > 10 
    and rn <=20 

它應用了rownum到內部查詢,然後你用行號11-20中選擇記錄條款WHERE

請注意,您應在兩個查詢中包含一個ORDER BY子句,以確保返回時記錄的順序是正確的。

0

NH 3.2在Oracle上有分頁問題(類似問題here)。我有同樣的問題,並繼續使用3.1,直到它被修復。

-1

我覺得Powerslave脂肪撥弄着例如最初由包括一個秩序,是這樣的:

SELECT * FROM 
    (SELECT row_.*, rownum rownum_ FROM 
     (
      SELECT * 
      FROM table 
      ORDER BY some_column 
     ) row_ 
     WHERE rownum <= 20) 
    WHERE rownum_ > 10; 

基本上什麼事情是NHibernate的生成查詢:

  1. 訂單結果
  2. 將這些限制爲前20個有序行
  3. 忘記訂單g ...
  4. ...然後需要無序結果11-20。

最終結果是,您將看到每個頁面的正確行數,但某些頁面有其他頁面上出現的行,並且也會有缺失的行。我已經對Oracle運行了NHibernate(4.0.3)生成的查詢來驗證所有這一切是真實的。

我已經提交了一個錯誤報告和補丁到NHibernate here。希望我的補丁是猶太教版,他們會在下一個版本中修復這個問題。

2

您可以創建自己的自定義Oracle方言來解決此問題,也可以等待接受NH-3814補丁。最好等待NHibernate接受補丁,但如果你沒有時間,可以這樣做。

https://nhibernate.jira.com/browse/NH-3814

該類將覆蓋從Oracle8iDialect的方法,以允許同時用於跳過和採取值rownum_字段。我不得不使用反射來調用ExtractColumnOrAliasNames,因爲這是一種私有方法。

internal class Oracle10gDialectExtended : NHibernate.Dialect.Oracle10gDialect 
{ 

    public Oracle10gDialectExtended() 
    { 
    } 

    /// <summary> 
    /// This is from patch NH-3814 
    /// https://nhibernate.jira.com/browse/NH-3814 
    /// https://nhibernate.jira.com/secure/attachment/24661/Oracle8iDialect.cs 
    /// </summary> 
    /// <param name="sql"></param> 
    /// <param name="offset"></param> 
    /// <param name="limit"></param> 
    /// <returns></returns> 
    public override NHibernate.SqlCommand.SqlString GetLimitString(NHibernate.SqlCommand.SqlString sql, NHibernate.SqlCommand.SqlString offset, NHibernate.SqlCommand.SqlString limit) 
    { 
     sql = sql.Trim(); 
     bool isForUpdate = false; 
     if (sql.EndsWithCaseInsensitive(" for update")) 
     { 
      sql = sql.Substring(0, sql.Length - 11); 
      isForUpdate = true; 
     } 

     string selectColumns = ExtractColumnOrAliasNames(sql); 

     var pagingSelect = new SqlStringBuilder(sql.Count + 10); 
     if (offset != null) 
     { 
      pagingSelect.Add("select " + selectColumns + " from (select row_.*, rownum rownum_ from ("); 
     } 
     else 
     { 
      pagingSelect.Add("select " + selectColumns + " from ("); 
     } 

     pagingSelect.Add(sql); 

     if (offset != null && limit != null) 
     { 
      pagingSelect.Add(") row_) where rownum_ <=").Add(limit).Add(" and rownum_ >").Add(offset); 
     } 
     else if (limit != null) 
     { 
      pagingSelect.Add(") where rownum <=").Add(limit); 
     } 
     else 
     { 
      // offset is specified, but limit is not. 
      pagingSelect.Add(") row_) where rownum_ >").Add(offset); 
     } 

     if (isForUpdate) 
     { 
      pagingSelect.Add(" for update"); 
     } 

     return pagingSelect.ToSqlString(); 
    } 

    private string ExtractColumnOrAliasNames(SqlString select) 
    { 
     BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public; 
     MethodInfo info = typeof(NHibernate.Dialect.Oracle8iDialect).GetMethod("ExtractColumnOrAliasNames", flags, null, new Type[] { typeof(SqlString) }, null); 

     if (info == null) 
     { 
      throw new MissingMethodException("Method ExtractColumnOrAliasNames does not exist on class NHibernate.Dialect.Oracle8iDialect"); 
     } 

     return info.Invoke(this, new object[] { select }) as string; 
    } 
}