证明DataReader分页的可行性
记得那是07年的一个下午,我正在网上瞎逛,突然看到一段代码,也就是跟楼主上面的代码类似的,通过DataReader来分页的代码。当时我吓了一跳,这样的代码,是不是稍大些的系统就不能用了呢?因为按我当时的理解,while (dr.Read()),若我的系统有几百万条的数据,那这个while也要转好久了,还要传数据,应该快不了的。可是后来经过我的测试,其实性能是很好的,至少不是我们想像中的那么慢的。
在那时候,我用我们系统里面的一个200多W的统计表进行了测试,只是简单的select * from table ,然后在程序里面 while 遍历,最后在GridView上面绑定了一下,效果很好。我记忆深刻,那会白天,在公司里面,前面几页运行良好,后面的页码,当然也包括最后一页,我都不敢去点,怕影响系统性能。等到了晚上回家,我半夜试了一下,居然跟前面几页差距不大,我那时候只是为了测试一下是否可行,也没有使用记时器,但是应该也是在5秒以内就返回了。前面的话,应该也是3,4秒的样子。太让我意外了,同时也太惊喜了。
不过因为系统框架里面都是使用的存储过程,也运行良好,也就一直没有去改过。也就是说,这套分页解决方案,在真正的大数据量下面,我也没有实际在项目中应用过,不过小项目倒是常用。
对于一般的系统来说,这个通用的分页解决方案就够用了。对于大一点的,可以通过其它手段,如分表或其它什么的,也能满足一般的应用。
想想发到首页,应该出点代码,就又花了些时间补充了一下。
下面是我的测试代码:
页面:简单的。
</asp:GridView>
<lcs:Pager ID="Pager1" runat="server" onpagechanged="Pager1_PageChanged" AlwaysShow="true"
CurrentPageButtonPosition="Center">
</lcs:Pager>
后台代码:也是简单的。
1 private void BindRpt()
3 int totalCount;
4 double beg = DateTime.Now.Ticks;
5 if (isDatareader)
6 {
7 GridView1.DataSource = LCS.Data.DbHelper.GetPager(
8 Pager1.PageSize, Pager1.CurrentPageIndex, "Statistic", "*", "StatisticID", false, out totalCount, null, null); ;
10 }
11 else
12 {
13 totalCount = LCS.Data.DbHelper.GetCount("Statistic", "");
14 GridView1.DataSource = LCS.Data.DbHelper.GetPager(
15 Pager1.PageSize, Pager1.CurrentPageIndex, "Statistic", "*", "StatisticID", false, null);
16 }
17 Response.Write("<hr/>" + (DateTime.Now.Ticks - beg)+ "<hr/>");
18
19 GridView1.DataBind();
20 Pager1.RecordCount = totalCount;
21 }
最后再附上我的DbHelper里面的方法实现:
先看使用datareader的
1 public static DataTable GetPager(int pageSize, int pageIndex,
3 out int totalCount, string condition, params object[] parmsValues
4 )
5 {
6 //select * from talble where 1=1 order by fld desc
7 //是标准的sql,不需要单独区分
8 string sql = "select " + fldName + " from " + tblName.ToString()
9 + ((string.IsNullOrEmpty(condition)) ? string.Empty : (" where 1=1 " + condition))
10 + " order by " + fldSort.ToString() + (isDesc ? " DESC" : " ASC");
11
12 using (DbDataReader reader = ExecuteReader(sql, parmsValues))
13 {
14 DataTable dt = new DataTable();
15 int fieldCount = reader.FieldCount;
16 for (int i = 0; i < fieldCount; i++)
17 {
18 DataColumn col = new DataColumn();
19 col.ColumnName = reader.GetName(i);
20 col.DataType = reader.GetFieldType(i);
21 dt.Columns.Add(col);
22 }
23 totalCount = 0;
24 int first = (pageIndex - 1) * pageSize + 1;
25 int last = pageIndex * pageSize;
26 while (reader.Read())
27 {
28 totalCount++;
29 if (totalCount >= first && last >= totalCount)
30 {
31 DataRow r = dt.NewRow();
32 for (int i = 0; i < fieldCount; i++)
33 {
34 r[i] = reader[i];
35 }
36 dt.Rows.Add(r);
37 }
38 }
39 return dt;
40 }
41 }
42
再看常规的:
1 public static DbDataReader GetPager(int pageSize, int pageIndex,
3 {
4 return ExecuteReader(Provider.GetPagerSql(pageSize, pageIndex, tblName, fldName, fldSort, isDesc, condition));
5 }
//我内部使用了一个格式化sql字符串参数的过程,所以这里有个中转。
2 {
3 if (format == null || format.Length == 0) throw new ArgumentNullException("commandText");
4 if ((parameterValues != null) && (parameterValues.Length > 0))
5 {
6 //当存在参数时,格式化参数
7 SQlParameterFormatter formatter = new SQlParameterFormatter();
8 formatter.Provider = Provider;
9 formatter.Format(format, parameterValues);
10 return ExecuteReader(CommandType.Text, formatter.Sql, formatter.Parameters);
11 }
12 else//无参数时直接掉用
13 {
14 return ExecuteReader(CommandType.Text, format, (DbParameter[])null);
15 }
16 }
17
//最后再看一下生成分页sql字符串的方法
1 public string GetPagerSql( int pageSize, int pageIndex,
3 {
4 string strSort = isDesc ? " DESC" : " ASC";
5
6 if (pageIndex == 1)
7 {
8 return "select top " + pageSize.ToString() + " " + fldName + " from " + tblName.ToString()
9 + ((string.IsNullOrEmpty(condition)) ? string.Empty : (" where " + condition))
10 + " order by " + fldSort.ToString() + strSort;
11 }
12 else
13 {
14 System.Text.StringBuilder strSql = new System.Text.StringBuilder();
15 strSql.AppendFormat("select top {0} {1} from {2} ", pageSize,fldName, tblName);
16 strSql.AppendFormat(" where {1} not in (select top {0} {1} from {2} ", pageSize * (pageIndex - 1),
17 (fldSort.Substring(fldSort.LastIndexOf(',') + 1, fldSort.Length - fldSort.LastIndexOf(',') - 1)), tblName);
18 if (!string.IsNullOrEmpty(condition))
19 {
20 strSql.AppendFormat(" where {0} order by {1}{2}) and {0}", condition, fldSort, strSort);
21 }
22 else
23 {
24 strSql.AppendFormat(" order by {0}{1}) ", fldSort, strSort);
25 }
26 strSql.AppendFormat(" order by {0}{1}", fldSort, strSort);
27 return strSql.ToString();
28 }
29 }
30
最后,给想直接看结果的一个连接:
http://jyt.dai8.net:89/test_cb.aspx
可别把我的电脑给搞死啦。
经过我的测试,常规的还是比datareader的要来得快,若单是从数值上面看的话,差距还蛮大的,大的差10多倍,小的也要差3,4倍 ,不过对于实用性来说,也是够用啦。因为很多时候,用户是感觉不到的,特别是那些客户端的,或是企业内部使用的,基本上没有并发的项目。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
· 全程使用 AI 从 0 到 1 写了个小工具
· 从文本到图像:SSE 如何助力 AI 内容实时呈现?(Typescript篇)