C#拼接SQL語句,SQL Server 2005+,多行多列大數據量情況下,使用ROW_NUMBER實現的高效分頁排序


如果項目中要用到數據庫,鐵定要用到分頁排序。

之前在做數據庫查詢優化的時候,通宵寫了以下代碼,來拼接分頁排序的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]
  1. Tbl_Product->ID(序號,非空,自增)
  2. Tbl_Product->ProductId(產品Id,主鍵)
  3. Tbl_Product->ProductName(產品名稱,非空)
  4. 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM