C#拼接SQL语句 用ROW_NUMBER实现的高效分页排序

时间:2023-12-27 04:47:01 

如果项目中要用到数据库,铁定要用到分页排序。之前在做数据库查询优化的时候,通宵写了以下代码,来拼接分页排序的SQL语句


/// <summary>
/// 单表(视图)获取分页SQL语句
/// </summary>
/// <param name="tableName">表名或视图名</param>
/// <param name="key">唯一键</param>
/// <param name="fields">获取的字段</param>
/// <param name="condition">查询条件(不包含WHERE)</param>
/// <param name="collatingSequence">排序规则(不包含ORDER BY)</param>
/// <param name="pageSize">页大小</param>
/// <param name="pageIndex">页码(从1开始)</param>
/// <returns>分页SQL语句</returns>
public static string GetPagingSQL(
string tableName,
string key,
string fields,
string condition,
string collatingSequence,
int pageSize,
int pageIndex)
{
string whereClause = string.Empty;
if (!string.IsNullOrEmpty(condition))
{
whereClause = string.Format("WHERE {0}", condition);
}

if (string.IsNullOrEmpty(collatingSequence))
{
collatingSequence = string.Format("{0} ASC", key);
}

StringBuilder sbSql = new StringBuilder();

sbSql.AppendFormat("SELECT {0} ", PrependTableName(tableName, fields, ','));
sbSql.AppendFormat("FROM ( SELECT TOP {0} ", pageSize * pageIndex);
sbSql.AppendFormat(" [_RowNum_] = ROW_NUMBER() OVER ( ORDER BY {0} ), ", collatingSequence);
sbSql.AppendFormat(" {0} ", key);
sbSql.AppendFormat(" FROM {0} ", tableName);
sbSql.AppendFormat(" {0} ", whereClause);
sbSql.AppendFormat(" ) AS [_TempTable_] ");
sbSql.AppendFormat(" INNER JOIN {0} ON [_TempTable_].{1} = {0}.{1} ", tableName, key);
sbSql.AppendFormat("WHERE [_RowNum_] > {0} ", pageSize * (pageIndex - 1));
sbSql.AppendFormat("ORDER BY [_TempTable_].[_RowNum_] ASC ");

return sbSql.ToString();
}

/// <summary>
/// 给字段添加表名前缀
/// </summary>
/// <param name="tableName">表名</param>
/// <param name="fields">字段</param>
/// <param name="separator">标识字段间的分隔符</param>
/// <returns></returns>
public static string PrependTableName(string tableName, string fields, char separator)
{
StringBuilder sbFields = new StringBuilder();

string[] fieldArr = fields.Trim(separator).Split(separator);
foreach (string str in fieldArr)
{
sbFields.AppendFormat("{0}.{1}{2}", tableName, str.Trim(), separator);
}

return sbFields.ToString().TrimEnd(separator);
}


假设有如下产品表:


CREATE TABLE [dbo].[Tbl_Product]
(
[ID] [int] IDENTITY(1, 1)
NOT NULL ,
[ProductId] [varchar](50) NOT NULL ,
[ProductName] [nvarchar](50) NOT NULL ,
[IsDeleted] [int] NOT NULL
CONSTRAINT [DF_Tbl_Product_IsDeleted] DEFAULT ( (0) ) ,
CONSTRAINT [PK_Tbl_Product] PRIMARY KEY CLUSTERED ( [ProductId] ASC )
WITH ( PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON ) ON [PRIMARY]
)
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语句如下(已手动调整了格式):


SELECT Tbl_Product.ID ,
Tbl_Product.ProductId ,
Tbl_Product.ProductName
FROM ( SELECT TOP 25
[_RowNum_] = ROW_NUMBER() OVER ( ORDER BY ProductName ASC, ID DESC ) ,
ID
FROM Tbl_Product
WHERE IsDeleted = 0
) AS [_TempTable_]
INNER JOIN Tbl_Product ON [_TempTable_].ID = Tbl_Product.ID
WHERE [_RowNum_] > 20
ORDER BY [_TempTable_].[_RowNum_] ASC


查询的字段列表,去掉了不关心的字段(这里为IsDeleted,因为条件里面IsDeleted=0,查出来的产品都是没被删除的);
排序依据,在调用该方法时,应尽量确保排序的依据可以唯一确定记录在结果集中的位置(这里添加了辅助排序依据,ID DESC,如果产品重名,添加的晚的排在前面);
性能优化的一点儿建议:如果字段的值是计算出来的,如:总价=单价*数量,而此时需要总价大于多少的记录,还得拿总价递增或者递减排序,如果不要临时表,数据量大的时候,就等着买新电脑吧!你问我为什么要买新电脑,哦,因为你会把现在的电脑砸掉!O(∩_∩)O~
另外一点儿建议,使用ROW_NUMBER时,切记一定要和“TOP n”一起使用,n等于int.MaxValue都比不加“TOP n”时要快。
最后,拜托哪位好心人士给测试下性能,拜托了,本人数据库菜鸟,不太懂得数据库的性能测试。

我只知道我对我写的分页排序还是很有信心的,(*^__^*) 嘻嘻!

首发:博客园->剑过不留痕

标签:拼接SQL,ROW,NUMBER
0
投稿

猜你喜欢

  • java反射之通过反射了解集合泛型的本质(详解)

    2023-02-04 03:10:25
  • Java Main 函数启动不退出的解决方案

    2022-03-24 14:25:03
  • DataGridView控件显示行号的正确代码及分析

    2022-07-26 22:05:50
  • 一篇文章彻底弄懂Java中二叉树

    2023-04-08 12:47:30
  • Unity使用多态制作计算器功能

    2022-10-15 02:50:06
  • 详解SpringBoot工程的三种搭建方式

    2022-09-20 21:47:23
  • Intellij搭建springmvc常见问题解决方案

    2023-07-23 12:53:29
  • Java8通过Function获取字段名的步骤

    2022-07-14 14:06:23
  • Maven插件的安装及使用

    2022-04-05 04:38:30
  • DevExpress TreeList 常见问题解决方法

    2022-07-15 18:16:54
  • spring cloud Feign使用@RequestLine遇到的坑

    2023-12-15 09:18:16
  • 浅谈一下Java中的悲观锁和乐观锁

    2023-08-12 05:54:27
  • Spring Boot支持Crontab任务改造的方法

    2023-08-08 20:20:24
  • 详解c# 多态

    2021-12-07 07:12:40
  • Android自定义Seekbar滑动条 Pop提示跟随滑动按钮滑动

    2021-11-02 20:32:06
  • 基于Java检查IPv6地址的合法性

    2022-08-05 12:17:17
  • Springboot+WebSocket实现一对一聊天和公告的示例代码

    2022-06-16 11:32:33
  • 浅谈Java由于不当的执行顺序导致的死锁

    2022-08-05 22:05:33
  • 使用SpringBoot获取resources文件路径

    2022-10-12 15:29:56
  • c#语言使用Unity粒子系统制作手雷爆炸

    2021-10-11 11:13:46
  • asp之家 软件编程 m.aspxhome.com