2013-01-15 49 views
10

我有一個數據庫超過200萬記錄,我需要執行一個分頁顯示在我的web應用程序上,每個頁面必須在DataGrid中有10個記錄。哪個是在SQL Server上執行分頁的最佳方法?

我已經嘗試過使用ROW_NUMBER(),但這種方式將選擇所有200萬條記錄,然後只獲得10條記錄。我也嘗試使用TOP 10,但我將不得不保存第一個和最後一個ID來控制頁面。我讀過使用DataAdapter.Fill()將選擇所有內容,然後獲得我需要的10條記錄。

哪一個是最好的方法?我應該使用DataAdapter.Fill()嗎?或者使用SQL Server的功能ROW_NUMBER()?或嘗試使用TOP 10

+4

看到這個http://blog.sqlauthority.com/2010/12/15/sql-server-server-side-paging-in-sql-server-2011-a-better-alternative/ –

+0

只讀: http://www.asp.net/web-forms/tutorials/data-access/paging-and-sorting/efficiently-paging-through-large-amounts-of-data-vb這是一個非常完整的教程(VB.NET但在這種情況下無關緊要)。 –

+0

@Guilherme你真的*還在使用sql-server-2000嗎? (標籤),如果是這樣,這將限制你的選擇相當多。 –

回答

4
ALTER PROCEDURE [dbo].[SP_tblTest_SelectSpecificRecordsWithCTE] 
    @FromRow int = 1000000, 
    @PgSize int = 10 
AS 
BEGIN 
    ;WITH RecordsRN AS 
    (
     select ID, colValue, ROW_NUMBER() over(order by colvalue) as Num from tblTest 
    ) 
    SELECT ID Value, colValue Text FROM RecordsRN WHERE Num between @FromRow AND (@[email protected]) 
END 

這是我使用分頁查詢。使用它,你會在4-5秒內得到你想要的10條記錄。我在3秒內獲得了10條記錄,而我的數據庫中的總記錄數是1000萬,不使用前10位,每次只能記錄10條記錄。在我的情況下,我維護頁面大小和會話中的起始行號(@FromRow),並將這兩個值傳遞給下面給定的存儲過程並獲得結果。 此外,如果您使用的是SQL 2012,您可能需要使用OFFSET並獲取下一行10行。在谷歌搜索關於OFFSET關鍵字,你會看到你想要的結果在上面。

感謝

+0

所有的答案都非常好,但是你和我所做的更類似,我仍然認爲這是最好的方法。不幸的是,我的SQL Server是2008年,所以我沒有使用OFFSET語句。謝謝。 –

1

我使用下面模式(自動地)生成分頁子查詢:

  • 子查詢概念性地跳過@skipN行,然後取:

    select top (@takeN) <your-column-list> 
    from (
        select qSub2.*, _row=row_number() over (order by SomeColumn Asc, SomethingElse Desc) 
        from (
         select top (@takeN + @skipN) <your-column-list> 
         from ( 
          select <your-subquery-here> 
         ) as qSub1 
         order by SomeColumn Asc, SomethingElse Desc 
        ) as qSub2 
    ) qSub3 
    where _row > @skipN 
    order by _row 
    

    在此圖案注接下來的@takeN行。

  • 如果您不關心結果中額外的列_row,您可以用*代替<your-column-list>;我使用顯式列列表是因爲它允許我在運行時對列集合進行子集劃分,僅查找主要關鍵列等。
  • 你的order by條款應該是相同的; sql server的optmizer通常足夠聰明,可以理解這一點。重複是用於截斷結果的top子句的副作用; top在未排序的子查詢中不合法。 top有助於查詢優化器理解此查詢可能返回少量行。
  • 使用@takeN@skipN而不是基於頁碼+尺寸的參數的原因相當小。首先,它在查詢中更加靈活一些,並且更簡單一些,其次,它使sql server的優勢更好一些:首先,DB對於優化這些查詢並不是特別精彩,希望是像這樣的外部簡單的頂部子句使得優化器理解可能的最大行數變得微不足道。一般來說,我儘量避免在sql中進行計算,我可以在代碼中同樣做得很好,因爲它傾向於混淆優化器(儘管在@ pagecount * @ pagesize的特定情況下實驗表明它不是一個大問題)

請注意,sql server 2012支持一個新的offset...fetch clause正是這種情況,這是非常簡單的。

2

使用ROW_NUMBER()並實現一個靜態實用功能(如我的代碼中的GetPaginatedSQL),它會自動將原始SQL查詢封裝到有限/分頁的查詢中。

這是我使用的一個:

namespace Persistence.Utils 
{ 
    public class SQLUtils 
    { 
     /// <summary> 
     /// Builds a paginated/limited query from a SELECT SQL. 
     /// </summary> 
     /// <param name="startRow">Start row</param> 
     /// <param name="numberOfRows">Number/quatity of rows to be expected</param> 
     /// <param name="sql">Original SQL (without its ordering clause)</param> 
     /// <param name="orderingClause">MANDATORY: ordering clause (including ORDER BY keywords)</param> 
     /// <returns>Paginated SQL ready to be executed.</returns> 
     /// <remarks>SELECT keyword of original SQL must be placed exactly at the beginning of the SQL.</remarks> 
     public static string GetPaginatedSQL(int startRow, int numberOfRows, string sql, string orderingClause) 
     { 
      // Ordering clause is mandatory! 
      if (String.IsNullOrEmpty(orderingClause)) 
       throw new ArgumentNullException("orderingClause"); 

      // numberOfRows here is checked of disable building paginated/limited query 
      // in case is not greater than 0. In this case we simply return the 
      // query with its ordering clause appended to it. 
      // If ordering is not spe 
      if (numberOfRows <= 0) 
      { 
       return String.Format("{0} {1}", sql, orderingClause); 
      } 
      // Extract the SELECT from the beginning. 
      String partialSQL = sql.Remove(0, "SELECT ".Length); 

      // Build the limited query... 
      return String.Format(
       "SELECT * FROM (SELECT ROW_NUMBER() OVER ({0}) AS rn, {1}) AS SUB WHERE rn > {2} AND rn <= {3}", 
       orderingClause, 
       partialSQL, 
       startRow.ToString(), 
       (startRow + numberOfRows).ToString() 
      ); 
     } 
    } 
} 

功能上面可能會得到改善,但最初的實現。

然後,在你的DAO,你應該只是讓這樣的事情:

using (var conn = new SqlConnection(CONNECTION_STRING)) 
{ 
    using (var cmd = conn.CreateCommand()) 
    { 
     String SQL = "SELECT * FROM MILLIONS_RECORDS_TABLE"; 
     String SQLOrderBy = "ORDER BY DATE ASC "; //GetOrderByClause(Object someInputParams); 
     String limitedSQL = GetPaginatedSQL(0, 50, SQL, SQLOrderBy); 

     DataSet ds = new DataSet(); 
     SqlDataAdapter adapter = new SqlDataAdapter(); 

     cmd.CommandText = limitedSQL; 

     // Add named parameters here to the command if needed... 

     adapter.SelectCommand = cmd; 
     adapter.Fill(ds); 

     // Process the dataset... 
    } 
    conn.Close(); 
} 

希望它能幫助。

相關問題