記得那是07年的一個下午,我正在網上瞎逛,突然看到一段代碼,也就是跟樓主上面的代碼類似的,通過DataReader來分頁的代碼。當時我嚇了一跳,這樣的代碼,是不是稍大些的系統就不能用了呢?因為按我當時的理解,while (dr.Read()),若我的系統有幾百萬條的數據,那這個while也要轉好久了,還要傳數據,應該快不了的。可是後來經過我的測試,其實性能是很好的,至少不是我們想像中的那麼慢的。
在那時候,我用我們系統裡面的一個200多W的統計表進行了測試,只是簡單的select * from table ,然後在程序裡面 while 遍歷,最後在GridView上面綁定了一下,效果很好。我記憶深刻,那會白天,在公司裡面,前面幾頁運行良好,後面的頁碼,當然也包括最後一頁,我都不敢去點,怕影響系統性能。等到了晚上回家,我半夜試了一下,居然跟前面幾頁差距不大,我那時候只是為了測試一下是否可行,也沒有使用記時器,但是應該也是在5秒以內就返回了。前面的話,應該也是3,4秒的樣子。太讓我意外了,同時也太驚喜了。
不過因為系統框架裡面都是使用的存儲過程,也運行良好,也就一直沒有去改過。也就是說,這套分頁解決方案,在真正的大數據量下面,我也沒有實際在項目中應用過,不過小項目倒是常用。
對於一般的系統來說,這個通用的分頁解決方案就夠用了。對於大一點的,可以通過其它手段,如分表或其它什麼的,也能滿足一般的應用。
想想發到首頁,應該出點代碼,就又花了些時間補充了一下。
下面是我的測試代碼:
頁面:簡單的。
- <asp:GridView ID="GridView1" runat="server">
- </asp:GridView>
- <lcs:Pager ID="Pager1" runat="server" onpagechanged="Pager1_PageChanged" AlwaysShow="true"
- CurrentPageButtonPosition="Center">
- </lcs:Pager>
後台代碼:也是簡單的。
- private void BindRpt()
- {
- int totalCount;
- double beg = DateTime.Now.Ticks;
- if (isDatareader)
- {
- GridView1.DataSource = LCS.Data.DbHelper.GetPager(
- Pager1.PageSize, Pager1.CurrentPageIndex, "Statistic", "*", "StatisticID",
- false, out totalCount, null, null); ;
- }
- else
- {
- totalCount = LCS.Data.DbHelper.GetCount("Statistic", "");
- GridView1.DataSource = LCS.Data.DbHelper.GetPager(
- Pager1.PageSize, Pager1.CurrentPageIndex, "Statistic", "*", "StatisticID", false, null);
- }
- Response.Write("<hr/>" + (DateTime.Now.Ticks - beg)+ "<hr/>");
- GridView1.DataBind();
- Pager1.RecordCount = totalCount;
- }
最後再附上我的DbHelper裡面的方法實現:
先看使用datareader的
- public static DataTable GetPager(int pageSize, int pageIndex,
- string tblName, string fldName, string fldSort, bool isDesc,
- out int totalCount, string condition, params object[] parmsValues
- )
- {
- //select * from talble where 11=1 order by fld desc
- //是標准的sql,不需要單獨區分
- string sql = "select " + fldName + " from " + tblName.ToString()
- + ((string.IsNullOrEmpty(condition)) ? string.Empty : (" where 11=1 " + condition))
- + " order by " + fldSort.ToString() + (isDesc ? " DESC" : " ASC");
- using (DbDataReader reader = ExecuteReader(sql, parmsValues))
- {
- DataTable dt = new DataTable();
- int fieldCount = reader.FieldCount;
- for (int i = 0; i < fieldCount; i++)
- {
- DataColumn col = new DataColumn();
- col.ColumnName = reader.GetName(i);
- col.DataType = reader.GetFieldType(i);
- dt.Columns.Add(col);
- }
- totalCount = 0;
- int first = (pageIndex - 1) * pageSize + 1;
- int last = pageIndex * pageSize;
- while (reader.Read())
- {
- totalCount++;
- if (totalCount >= first && last >= totalCount)
- {
- DataRow r = dt.NewRow();
- for (int i = 0; i < fieldCount; i++)
- {
- r[i] = reader[i];
- }
- dt.Rows.Add(r);
- }
- }
- return dt;
- }
- }
再看常規的:
- public static DbDataReader GetPager(int pageSize, int pageIndex,
- string tblName, string fldName, string fldSort, bool isDesc, string condition)
- {
- return ExecuteReader(Provider.GetPagerSql(pageSize, pageIndex, tblName, fldName,
- fldSort, isDesc, condition));
- }
- //我內部使用了一個格式化sql字符串參數的過程,所以這裡有個中轉。
- public static DbDataReader ExecuteReader(string format, params object[] parameterValues)
- {
- if (format == null || format.Length == 0) throw new ArgumentNullException("commandText");
- if ((parameterValues != null) && (parameterValues.Length > 0))
- {
- //當存在參數時,格式化參數
- SQlParameterFormatter formatter = new SQlParameterFormatter();
- formatter.Provider = Provider;
- formatter.Format(format, parameterValues);
- return ExecuteReader(CommandType.Text, formatter.Sql, formatter.Parameters);
- }
- else//無參數時直接掉用
- {
- return ExecuteReader(CommandType.Text, format, (DbParameter[])null);
- }
- }
//最後再看一下生成分頁sql字符串的方法
- public string GetPagerSql( int pageSize, int pageIndex,
- string tblName,string fldName,string fldSort, bool isDesc,string condition)
- {
- string strSort = isDesc ? " DESC" : " ASC";
- if (pageIndex == 1)
- {
- return "select top " + pageSize.ToString() + " " + fldName + " from " + tblName.ToString()
- + ((string.IsNullOrEmpty(condition)) ? string.Empty : (" where " + condition))
- + " order by " + fldSort.ToString() + strSort;
- }
- else
- {
- System.Text.StringBuilder strSql = new System.Text.StringBuilder();
- strSql.AppendFormat("select top {0} {1} from {2} ", pageSize,fldName, tblName);
- strSql.AppendFormat(" where {1} not in (select top {0} {1} from {2} ",
- pageSize * (pageIndex - 1),
- (fldSort.Substring(fldSort.LastIndexOf(',') + 1,
- fldSort.Length - fldSort.LastIndexOf(',') - 1)),
- tblName);
- if (!string.IsNullOrEmpty(condition))
- {
- strSql.AppendFormat(" where {0} order by {1}{2}) and {0}", condition, fldSort, strSort);
- }
- else
- {
- strSql.AppendFormat(" order by {0}{1}) ", fldSort, strSort);
- }
- strSql.AppendFormat(" order by {0}{1}", fldSort, strSort);
- return strSql.ToString();
- }
- }
最後,給想直接看結果的一個連接:http://jyt.dai8.net:89/test_cb.aspx
可別把我的電腦給搞死啦。
經過我的測試,常規的還是比datareader的要來得快,若單是從數值上面看的話,差距還蠻大的,大的差10多倍,小的也要差3,4倍 ,不過對於實用性來說,也是夠用啦。因為很多時候,用戶是感覺不到的,特別是那些客戶端的,或是企業內部使用的,基本上沒有並發的項目。