如果項目中要用到數據庫,鐵定要用到分頁排序。
之前在做數據庫查詢優化的時候,通宵寫了以下代碼,來拼接分頁排序的SQL語句。
1 /// <summary> 2 /// 單表(視圖)獲取分頁SQL語句 3 /// </summary> 4 /// <param name="tableName">表名或視圖名</param> 5 /// <param name="key">唯一鍵</param> 6 /// <param name="fields">獲取的字段</param> 7 /// <param name="condition">查詢條件(不包含WHERE)</param> 8 /// <param name="collatingSequence">排序規則(不包含ORDER BY)</param> 9 /// <param name="pageSize">頁大小</param> 10 /// <param name="pageIndex">頁碼(從1開始)</param> 11 /// <returns>分頁SQL語句</returns> 12 public static string GetPagingSQL( 13 string tableName, 14 string key, 15 string fields, 16 string condition, 17 string collatingSequence, 18 int pageSize, 19 int pageIndex) 20 { 21 string whereClause = string.Empty; 22 if (!string.IsNullOrEmpty(condition)) 23 { 24 whereClause = string.Format("WHERE {0}", condition); 25 } 26 27 if (string.IsNullOrEmpty(collatingSequence)) 28 { 29 collatingSequence = string.Format("{0} ASC", key); 30 } 31 32 StringBuilder sbSql = new StringBuilder(); 33 34 sbSql.AppendFormat("SELECT {0} ", PrependTableName(tableName, fields, ',')); 35 sbSql.AppendFormat("FROM ( SELECT TOP {0} ", pageSize * pageIndex); 36 sbSql.AppendFormat(" [_RowNum_] = ROW_NUMBER() OVER ( ORDER BY {0} ), ", collatingSequence); 37 sbSql.AppendFormat(" {0} ", key); 38 sbSql.AppendFormat(" FROM {0} ", tableName); 39 sbSql.AppendFormat(" {0} ", whereClause); 40 sbSql.AppendFormat(" ) AS [_TempTable_] "); 41 sbSql.AppendFormat(" INNER JOIN {0} ON [_TempTable_].{1} = {0}.{1} ", tableName, key); 42 sbSql.AppendFormat("WHERE [_TempTable_].[_RowNum_] > {0} ", pageSize * (pageIndex - 1)); 43 sbSql.AppendFormat("ORDER BY [_TempTable_].[_RowNum_] ASC "); 44 45 return sbSql.ToString(); 46 } 47 48 /// <summary> 49 /// 給字段添加表名前綴 50 /// </summary> 51 /// <param name="tableName">表名</param> 52 /// <param name="fields">字段</param> 53 /// <param name="separator">標識字段間的分隔符</param> 54 /// <returns></returns> 55 public static string PrependTableName(string tableName, string fields, char separator) 56 { 57 StringBuilder sbFields = new StringBuilder(); 58 59 string[] fieldArr = fields.Trim(separator).Split(separator); 60 foreach (string str in fieldArr) 61 { 62 sbFields.AppendFormat("{0}.{1}{2}", tableName, str.Trim(), separator); 63 } 64 65 return sbFields.ToString().TrimEnd(separator); 66 }
假設有如下產品表:
1 CREATE TABLE [dbo].[Tbl_Product] 2 ( 3 [ID] [int] IDENTITY(1, 1) 4 NOT NULL , 5 [ProductId] [varchar](50) NOT NULL , 6 [ProductName] [nvarchar](50) NOT NULL , 7 [IsDeleted] [int] NOT NULL 8 CONSTRAINT [DF_Tbl_Product_IsDeleted] DEFAULT ( (0) ) , 9 CONSTRAINT [PK_Tbl_Product] PRIMARY KEY CLUSTERED ( [ProductId] ASC ) 10 WITH ( PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, 11 IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, 12 ALLOW_PAGE_LOCKS = ON ) ON [PRIMARY] 13 ) 14 ON [PRIMARY]
- Tbl_Product->ID(序號,非空,自增)
- Tbl_Product->ProductId(產品Id,主鍵)
- Tbl_Product->ProductName(產品名稱,非空)
- Tbl_Product->IsDeleted(虛擬刪除標記,非空)
調用BasicFunction.GetPagingSQL("Tbl_Product", "ID", "ID,ProductId,ProductName", "IsDeleted=0", "ProductName ASC, ID DESC", 5, 5),BasicFunction為分頁排序方法所在的靜態類,生成的分頁排序SQL語句如下(已手動調整了格式):
1 SELECT Tbl_Product.ID , 2 Tbl_Product.ProductId , 3 Tbl_Product.ProductName 4 FROM ( SELECT TOP 25 5 [_RowNum_] = ROW_NUMBER() OVER ( ORDER BY ProductName ASC, ID DESC ) , 6 ID 7 FROM Tbl_Product 8 WHERE IsDeleted = 0 9 ) AS [_TempTable_] 10 INNER JOIN Tbl_Product ON [_TempTable_].ID = Tbl_Product.ID 11 WHERE [_TempTable_].[_RowNum_] > 20 12 ORDER BY [_TempTable_].[_RowNum_] ASC
- 查詢的字段列表,去掉了不關心的字段(這里為IsDeleted,因為條件里面IsDeleted=0,查出來的產品都是沒被刪除的);
- 排序依據,在調用該方法時,應盡量確保排序的依據可以唯一確定記錄在結果集中的位置(這里添加了輔助排序依據,ID DESC,如果產品重名,添加的晚的排在前面);
- 性能優化的一點兒建議:如果字段的值是統計出來的,通常是在視圖中,且結果集很大,且需要對結果集分頁排序,可以使用臨時表規避不穩定的查詢效率(關於不穩定的查詢效率,會在之后的文章中示例);
- 另外一點兒建議,使用ROW_NUMBER時,切記一定要和“TOP n”一起使用,n等於int.MaxValue都比不加“TOP n”時要快。
最后,拜托哪位好心人士給測試下性能,拜托了,本人數據庫菜鳥,不太懂得數據庫的性能測試。
我只知道我對我寫的分頁排序還是很有信心的,(*^__^*) 嘻嘻!
首發:博客園->劍過不留痕,歡迎轉載,前提是注明出處:http://www.cnblogs.com/return8023/archive/2012/05/20/2510983.html