Efficient Paging for GridView
The GridView is a flexible control thatâs quickly becoming my favorite. It makes so many things a breeze. One of these things happens to be pagination. All you need to do to get the data in pages is to add the following 2 lines (the second one being optional):
<asp:GridViewID="PagedGridView"AllowPaging=truePageSize=25EnableViewState=falserunat="server"></asp:GridView>
Looks too good to be true? Unfortunately, it is. The way that GridView does pagination is horribly inefficient: it gets all the records from the database, then goes on to discard all the records that it doesnât need. And it does this every time the page loads. Which doesnât matter much if your table contains only 100 records, but if it contains 10,000 records or 50,000 records, then it starts to become a problem.
Fortunately, creating a more efficient paging for the GridView is easy, though we need to write more than two lines to make it work.####Getting Back Only the Records That We Want
To make the pagination process more efficient, we want to get from the database only those records that we actually need. Duh! Weâll do this using a nice SQL function called ROWNUMBER(). Unfortunately, this function is available only in SQL Server 2005, which not everyone has. We canât simply substitute a increment-by-one primary key column for ROWNUMBER() because weâll probably be deleting rows in the middle, which will screw up anything. If you donât have SQL Server 2005, hereâs a link to a tutorial that shows how to get the same result using a temporary table.
The stored procedure that does use ROW_NUMBER():
ALTER PROCEDURE dbo.EfficientGridViewPaging@pageIndex int,@pageSize intASBEGINWITH Entries AS (SELECT ROW_NUMBER() OVER (ORDER BY ID DESC)AS RowNumber, random_data1, random_data2, random_data_etcFROM the_table)SELECT random_data1, random_data2, random_data_etcFROM EntriesWHERE RowNumber BETWEEN (@pageIndex - 1) * @pageSize + 1 AND @pageIndex * @pageSizeEND
I first came across ROWNUMBER() in article while browsing the Internet as a means of procrastination learning more on SQL Server 2005. (Actually, to be technically accurate, I first learned about ROWNUMBER() while studying for Oracle certification, but I forgot about it completely until the article refreshed my memory. Hehe.) I created a stored procedure from scratch, feeling rather awesome. But as often happens, I found out that I wasnât the only person to have this problem.
The stored procedure takes in two parameters (the current page and the maximum number of records to return) and returns the appropriate records.
Total Number of Pages
One rather inconvenient fact is that GridViewâs PageCount property is read-only. This means that we need to figure out a way to get the total number of pages. Most implementations I saw have the total number of records as the output parameter of our afore-mentioned stored procedure. But as itâs unlikely that the number of records changes frequently, I chose to create a stored procedure that gets the number of records in the table and store it in the Session variable.
Creating the Links
With that out of the way, creating the links to different pages is fairly straight-forward. Hereâs a somewhat sloppy implementation that allows you to go to the first page, the previous page, the next page, or the last page:
using System;using System.Data;using System.Configuration;using System.Web;using System.Web.Security;using System.Web.UI;using System.Web.UI.WebControls;using System.Web.UI.WebControls.WebParts;using System.Web.UI.HtmlControls;using System.Data.Sql;using System.Data.SqlClient;public partial class _Default : System.Web.UI.Page{private int pageCount = 0;private int pageIndex = 0;private int pageSize = 25;private string dbConn = "Your Connection String â should be in web.config";protected void Page_Load(object sender, EventArgs e){if (Request["page"] == null)pageIndex = 1;elsepageIndex = Int16.Parse(Request["page"]);if (Session["pageCount"] == null){//Get the total number of pages in the tableusing (SqlConnection cn = new SqlConnection(dbConn)){cn.Open();SqlCommand cmd = cn.CreateCommand();cmd.CommandText = "GetTotalPages";cmd.CommandType = CommandType.StoredProcedure;//The stored procedure returns total number of recordspageCount = Convert.ToInt16(cmd.ExecuteScalar());}pageCount = pageCount / pageSize;Session["pageCount"] = pageCount;}elsepageCount = (int)Session["pageCount"];GetCurrentPageData();}private void GetCurrentPageData(){//Do error processing hereif (pageIndex > pageCount || pageIndex < 1)return;SqlDataSource sds = new SqlDataSource();sds.ConnectionString = dbConn;sds.SelectCommand = "EfficientGridViewPaging";sds.SelectCommandType = SqlDataSourceCommandType.StoredProcedure;sds.SelectParameters.Add("pageIndex", pageIndex.ToString());sds.SelectParameters.Add("pageSize", pageSize.ToString());PagedGridView.DataSource = sds;PagedGridView.DataBind();UpdatePagination();}private void UpdatePagination(){//Update the hrefs to point to the right pageFirstPageLink.HRef = "Default.aspx?page=1";PrevPageLink.HRef = "Default.aspx?page=" + (pageIndex - 1);NextPageLink.HRef = "Default.aspx?page=" + (pageIndex + 1);LastPageLink.HRef = "Default.aspx?page=" + pageCount;//Weâre on first pageif (pageIndex <= 1){FirstPageLink.HRef = String.Empty;PrevPageLink.HRef = String.Empty;}//Weâre on last pageelse if (pageIndex >= pageCount){NextPageLink.HRef = String.Empty;LastPageLink.HRef = String.Empty;}CurrentPage.Text = "Page " + pageIndex.ToString() + " of " + pageCount.ToString();}}