使用传入的总记录数实现一条sql语句完成分页查询_.NET_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > .NET > 使用传入的总记录数实现一条sql语句完成分页查询

使用传入的总记录数实现一条sql语句完成分页查询

 2013/8/12 18:07:59  xuefly  博客园  我要评论(0)
  • 摘要:问题:在传统的分页查询的实现中不可避免的需要两条sql语句,一条用于查询数据一条用于查询总记录数。如下面的实际代码所示:Img1当然如果使用存储过程的话是可以渐少一次数据库来回的,但是它依然是两条sql语句,一条查数据一条查总记录数。但我们在架构层面拒绝使用存储过程,因为它将逻辑分散在了应用程序和数据库中,不好维护,并且在SqlServer2008中也没有足够的证据证明存储过程更高效。很多人的直觉认为查询总记录数的那行没有必要,但是直觉只是第一步,项目中需要的是解决方案。解决方案
  • 标签:实现 使用 SQL SQL语句

问题:在传统的分页查询的实现中不可避免的需要两条sql语句,一条用于查询数据一条用于查询总记录数。如下面的实际代码所示:

Img1

当然如果使用存储过程的话是可以渐少一次数据库来回的,但是它依然是两条sql语句,一条查数据一条查总记录数。但我们在架构层面拒绝使用存储过程,因为它将逻辑分散在了应用程序和数据库中,不好维护,并且在SqlServer2008中也没有足够的证据证明存储过程更高效。

很多人的直觉认为查询总记录数的那行没有必要,但是直觉只是第一步,项目中需要的是解决方案。

解决方案:

从客户端输入总记录数,从而避免一次对总记录数的查询。首先将分页查询的参数封装为一个简单对象PagingInput,它属于模型分类中的输入模型类别所以以Input作为命令的后缀,其类图如下:

Img2

pagingInput的代码

class="code_img_closed" src="/Upload/Images/2013081218/0015B68B3C38AA5B.gif" alt="" />logs_code_hide('6e5ee1e6-3d15-42ea-b995-d4a06e29ed9c',event)" src="/Upload/Images/2013081218/2B1B950FA3DF188F.gif" alt="" />
 1 namespace jDTS {
 2     /// <summary>
 3     /// 分页输入参数
 4     /// </summary>
 5     public class PagingInput {
 6         /// <summary>
 7         /// 构造分页参数
 8         /// </summary>
 9         /// <param name="pageIndex">页索引</param>
10         /// <param name="pageSize">页尺寸</param>
11         /// <param name="sortField">排序字段</param>
12         /// <param name="sortOrder">排序方向</param>
13         public PagingInput(int pageIndex, int pageSize, string sortField, string sortOrder) {
14             this.pageIndex = pageIndex;
15             this.pageSize = pageSize;
16             this.sortField = sortField;
17             this.sortOrder = sortOrder;
18         }
19 
20         /// <summary>
21         /// 构造分页参数
22         /// </summary>
23         /// <param name="pageIndex">页索引</param>
24         /// <param name="pageSize">页尺寸</param>
25         /// <param name="sortField">排序字段</param>
26         /// <param name="sortOrder">排序方向</param>
27         /// <param name="total">总记录数</param>
28         public PagingInput(int pageIndex, int pageSize, string sortField, string sortOrder, int? total)
29             : this(pageIndex, pageSize, sortField, sortOrder) {
30             this.total = total;
31         }
32 
33         /// <summary>
34         /// 页索引。零基索引,即第一页对应0
35         /// </summary>
36         public int pageIndex { get; set; }
37 
38         /// <summary>
39         /// 页尺寸
40         /// </summary>
41         public int pageSize { get; set; }
42 
43         /// <summary>
44         /// 排序字段
45         /// </summary>
46         public string sortField { get; set; }
47 
48         /// <summary>
49         /// 排序方向
50         /// </summary>
51         public string sortOrder { get; set; }
52 
53         /// <summary>
54         /// 归档数据有一个特性,就是它的总记录数是不改变的,所以传入total以渐少一次数据库count查询
55         /// </summary>
56         public long? total { get; set; }
57 
58         /// <summary>
59         /// 查看total字段是否为空或者0
60         /// </summary>
61         public bool IsTotalNullOrZero { get { return !total.HasValue || total.Value == 0; } }
62 
63         /// <summary>
64         /// pageSize * pageIndex的计算值
65         /// </summary>
66         public int SkipCount { get { return pageSize * pageIndex; } }
67 
68         /// <summary>
69         /// 判断分页输入参数是否合法
70         /// </summary>
71         /// <returns></returns>
72         public bool IsValid() {
73             if (string.IsNullOrEmpty(sortField) || string.IsNullOrEmpty(sortOrder)) {
74                 throw new jDTSException("排序是必须的");
75             }
76             if (sortOrder != "asc" && sortOrder != "desc") {
77                 throw new jDTSException("排序方向只能是asc或desc,小写形式");
78             }
79             return true;
80         }
81     }
82 }
View Code

将GetPlistNodeELementPermissionTrs的与分页有关的形参替换为PagingInput类,重构后的代码如下:

Img3

相应的表现层的代码也需要做调整,从表现层收集来自界面层输入的total参数,注意total是可空的类型(int?)所以界面层可以选择是否传入:

Img4

现在服务端代码已经实现了一个接收可空的total参数的契约,如果本模块确实要使用这个一次查询机制的话则只需在客户端传入total参数即可。像下面这样在beforload事件中附加一个total参数:这里使用的是国产的miniui框架,成熟的js表现层框架都有类似的事件

Img5

另外要记得在前台的js方法search中将total置为0,因为search不是翻页,调用search即表示查询条件有变化,查询条件变化则总记录数通常会变化,所以需将total置为0表示让服务器端进行count查询(还记得前文的PagingInput模型上有个只读的IsNullOrZero属性吗?服务器端就是根据它来判断是否要count一次数据库的)。下面是归档模块中的一个search方法,它在每次search的时候都将传向服务器端的total参数置为0:

Img6

适用场景:

适用这个解决方案的场景并不常见,因为它要求查询的总记录数是固定的或者是不频繁改变的,在我们的项目中目前发现有两个场景适用:1查询归档档案(Record),因为数据归档后它的总记录数是不变的;2查询本人的登录记录(VisitingLog),因为自己的登录次数在下次登录的时候才会改变。

当然如果界面上配上一个刷新按钮,刷新的时候将传向服务器端的total参数置为0的话,增删改记录不频繁的模块也是可以这么干的。

发表评论
用户名: 匿名