Click here to Skip to main content
Click here to Skip to main content

Data paging in the database (an ASP.NET approach)

By , 27 Jul 2002
 

Sample Image - DBPaging.jpg

Introduction

When you have to display a large number of records, the common practice is to use data paging so the information will be presented in a more user friendly way. There are many solutions that one can use to implement such a system, each of them have advantages and disadvantages. In this article I will present a data paging solution and I will explain how it works and what you gain and what you loose using this technique.

This solution differ from others because the paging is on the database (data layer) not on the middle layer therefore when you navigate between pages only the current page records will be retrieved from database. When you do a data paging on the middle layer you need to retrieve all the records from the database and display only a part of them which doesn't sound that good when you have a huge numbers of records.

How it works

You need to have in the database a "container" table which will hold the "keys" for all records that will be displayed in the pages. Also that table must contain a key that will identify the user in this case I used the ASP.NET session id.
You need two stored procedures one used to init the pages and which will be only called at the beginning when the user first enters the .aspx page in our example, and the second one is the stored procedure used to retrieve the displayed records. When the user navigates between pages only the second stored procedure will be used.

Database

In the database I have two tables Members and TempPages. The Members table has the information that will be displayed and the TempPages is the container table that I will use for holding the pages for each user that access the site.

The stored procedures in my example are InitPages (the initialisation stored procedure) and GetPage (the SP for retrieving the records).

Members Table

The table in my example is presented in the script below. This table will hold the information which will be paged.

CREATE TABLE [Members] (
    [UserID] [numeric](18, 0) IDENTITY (1, 1) NOT NULL ,
    [UserName] [varchar] (25) ,
    [Password] [varchar] (25) ,
    [FullName] [varchar] (25) ,
    [Email] [varchar] (25) 
) ON [PRIMARY]
GO

TempPages Table

TempPages has a compose key between SessionID, used to identify the user session and RecordNo which is used to number the records starting from 1 .. RecordCount. User_ID collumn will hold the Members.UserID.

CREATE TABLE [TempPages] (
    [SessionID] [varchar] (24)  NOT NULL ,
    [RecordNo] [int] NOT NULL ,
    [User_ID] [int] NOT NULL 
) ON [PRIMARY]
GO

ALTER TABLE [TempPages] WITH NOCHECK ADD 
    CONSTRAINT [PK_TempPages] PRIMARY KEY  CLUSTERED 
    (
        [SessionID],
        [RecordNo]
    )  ON [PRIMARY] 
GO

InitPages stored precedure

To initialize which records to be displayed InitPages will be run only once at the beginning. The stored procedure will select the UserIDs from the Members table and will insert them into TempPages. In my example this stored precedure has only one parameter and that is @SessionID , the session identifier. You can add more pramaters for filtering the records that will be page, in this case I select all the records from Members.

InitPages works like this:

First will delete from the TempPages all the records that have SessionID equal with the current @SessionID. In case of a new initialization we delete the records from previous initializations.

After the table is clean I select all the Members.UserID and create a temporary table #TempPages that will hold all the Members.UserIDs plus an IDENTITY field which will later be the RecordNo from TempPages.

Then I select all the rows from the #TempPages and insert them into TempPages with the current session id (@SessionID). I select the @@ROWCOUNT and then drop the #TempPages.

The code for InitPages stored procedure is presented below:

CREATE PROCEDURE InitPages 
    @SessionID varchar(24)
AS

SET NOCOUNT ON

DECLARE @RecordCount int
SET @RecordCount = 0

--Deletes the previous records associated with the current session 
DELETE TempPages WHERE SessionID = @SessionID

-- Create a temporary table with all the UserIDs from Members.
SELECT 
    IDENTITY(int, 1, 1) AS RecordNo,
    CONVERT(int, UserID) AS UserID
INTO #TempPages  
FROM Members
ORDER BY UserID

-- Insert into TempPages the records from #TempPages and with the @SessionID
INSERT INTO TempPages( SessionID, RecordNo, User_ID )
SELECT @SessionID, RecordNo, UserID
FROM #TempPages
ORDER BY RecordNo

SELECT @RecordCount = @@ROWCOUNT

-- Drop the temporary table #TempPages
DROP TABLE #TempPages

-- Return the record count.
SELECT @RecordCount AS RecordCount
GO

GetPage stored procedure

This one is really simple. It needs only 3 parameters: @SessionID, @PageNo and @RowsPerPage. @SessiondID is used to identify the session. @PageNo is the page number and @RowsPerPage is the maximum number of records which will be displayed on each page.

The code for this stored procedure is presented below:

CREATE PROCEDURE GetPage
    @SessionID    varchar(24),
    @PageNo       int,
    @RowsPerPage  int
AS
SET NOCOUNT ON
DECLARE
  @Start      int,
  @End        int

-- Calculate the boundaries between which I will select 
SET @Start = (@PageNo - 1) * @RowsPerPage + 1
SET @End = @PageNo * @RowsPerPage

-- Select all the records from Members and join TempPages
SELECT
  Members.*
FROM 
  TempPages TP INNER JOIN Members ON TP.User_ID = Members.UserID
WHERE
  TP.SessionID = @SessionID AND
  TP.RecordNo >= @Start AND
  TP.RecordNo <= @End
ORDER BY RecordNo
GO

ASP.NET implementation

In ASP.NET I used a data grid to display the returned records from database. The datagrid is not involved in paging.

For database access and to work with stored procedures I used the Microsoft Data Access Application Block for .NET which is a single .NET assembly. It has all the necessary functionality for data access for Microsoft SQL Server. The advantages using Microsoft Data Access Application Block (mdaab) is that you write less code for data access :)) and your data access is optimized.

You can read more about this on Data Access Application Block for .NET. You can download from there the installer. I attached the DLL to the source code too.

The demo program is simple. The first time you request the default.aspx without querystring, the InitPages is executed, then after the records were initialized the GetPage stored procedure is executed. When you navigate between pages the PageNo and the PageCount is transmited by QueryString and if the values are not null then I execute only GetPage SP. See source code for more details.

Also in the Session_End I execute a clean up stored procedure which deletes from the TempPages the records that match the current Session.SessionID.ToString().

Overview

Advantages using this method

  • Works great for large number of recordsets. By retrieving the only the current page records from database the load on IIS is reduced alot. This solution is preferred than the paging in ADO/ADO.NET, if you have 100,000 records and you want see 20 on each page then only 20 records will be selected from the database.
  • If the result need a complicated SQL code, then this code is executed only once at the initialization and when you navigate between pages only the simple and a fast SELECT is executed ( GetPage SP ).

Disadvantages

  • If more concurrent users perform operation like ADD, EDIT, DELETE on the paged records the ADD operation is not felt by a user who navigates between pages. The user will see the new added records in the database only after the records are reinitialized. The EDIT and DELETE operations are ok because I do a JOIN on TempPages.User_ID = Members.UserID
  • Requires another table in the database (TempPages) and a clean up solution.

Remarks

If in your application you have to display different types of records then you could add a new column in the TempPages that will identify what kind of records and from where to be selected. For example on your site you want to page the members and the articles.

Feel free to post your comments and questions :)

Happy Programming!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Horatiu CRISTEA
Web Developer
Romania Romania
Member
Cool | :cool:

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralThis method is good...membertheJazzyBrain22 Oct '04 - 23:48 
... only for small recordsets.
 
I tested this method on a simple table with 1000000 records and it took about 2 minutes to execeute on a midle range laptop.
 
Although, it is easy and I have been using this paging method for a long time, when it comes to large tables it is useless. The thing is that all 1000000 records have to be inserted into the temp table. Imagine if I had a more complex query with joins etc...
 
I am looking for some more efficient way of paging,,,
 
Thank you
 
theJazzyBrain
Excellence is not an act, but a habit!
Aristotle

Generalthis method isn't suit for the large tabelmemberBing Ding9 Jun '03 - 23:37 
when the table is large etc have 1000000 records , select * into a temp table is a bad idea..
 
can you tell me a good method to paging records if it's large enough?etc 100,0000 records
 
sorry for my poor english,because i am a chinese and not good at english.
QuestionNo data shows from the TempPages Table?? What am I doing wrong??memberjcm26 Nov '02 - 13:57 

Open Microsoft Visual Studio.NET Enterprise Ed. 2002, Version 7.0.9466
File => New => Project => Visual C# Projects => ASP.NET Web Application
Location: Rename => (http://localhost/WebApplication1) to http://localhost/WA_DBPaging
View => Solution Explorer => "Right-Click" WebForm1.aspx => Rename to Default.aspx
View => Server Explorer => Expand Server(s) => Expand SQL Servers => Expand Selected SQL Server => Add DB
Right-Click Selected SQL Server => Create New DB => Enter DB Name DBPaging => Use SQL Auth => sa => sa's-pw
Save Changes and Close Visual Studio.NET and again, if asked to, "Save Changes"
 
Open Microsoft SQL2000 => Query Analyzer
Connect to SQL Server => SQL Server => SQL Server Authentication => sa => sa's-pw
Select DBPaging from Dropdown from Query Window Toolbar
File => Open => Open Query File => "location of downloaded file DBPaging.sql" => click Open
Click the "blue check" on the Toolbar to test SQL File ( Should get "The commands completed successfully" )
Click the "green > right arrow" on the Toolbar to Execute the loaded SQL File ( Again should get "Commands Success" )
Close SQL Query Analyzer
 
Open Microsoft Windows File Explorer
Navigate to the Directory location DBPaging_src.zip
If you have a Win Zip Utility installed on your computer, expand/Unzip the DBPaging_src.zip file on your computer.
Copy the file "Microsoft.ApplicationBlocks.Data.dll" from the unzipped "BIN" Directory to the "BIN" Directory under your "localhost" web directory located inside the current project directory "WA_DBPaging\bin"
Navigate to the location of the Unzipped contents of DBPaging_src.zip and copy the following files to the "localhost" web directory located inside the current project directory "WA_DBPaging"...;
Close Windows File Explorer
 
Open Microsoft Visual Studio.NET Enterprise Ed. 2002, Version 7.0.9466
File => Recent Projects => WA_ DBPaging.sln
View => Solution Explorer => "Right-Click" References => Add Reference => Browse => Double Click current project's "BIN" Directory => Select the file "Microsoft.ApplicationBlocks.Data.dll" => Click "OPEN" => Click "OK"
View => Server Explorer => Right-Click "Data Connections" => Add Connection => Enter Servername => Use Specific Name & Password => enter sa & sa's pw => Select DB on the Server to be "DBPaging" => Test Connection - Success => click OK => Click File => Save All.
View => Solution Explorer => "Double-Click" Global.asax => "Right-Click" View Code => Change the Following Code => private const string CONN_STRING = "Initial Catalog=;Data Source=;USER ID=SA;password=;"; to match your DB and Server...
View => Solution Explorer => "Double-Click" Default.asax => "Right-Click" View Code => Change the Following Code => private const string CONN_STRING = "Initial Catalog=;Data Source=;USER ID=SA;password=;"; to match your DB and Server...
View => Server Explorer => Right-Click "Data Connections" => Expand DBPaging.dbo => Expand Tables => Expand TempPages => Select/Highlight CTRL-Click all of the following Columns in TempPages => SessionID, RecordNo & User_ID. => Drag all of these with your mouse over to the Default.asax form.
In the Default.asax form, right-click on sqlDataAdapter1 => Select Configure DataAdapter => DataAdapter to use in Dropdown DBPaging.dbo => Use SQL Statements => Click Next in Data-to-load => Generated SELECT, Table Mappings, INSERT, UPDATE, and DELETE Statements => Finish
Again In the Default.asax form, right-click on sqlDataAdapter1 => Select Generate Dataset => Select New => DataSet1 => Add this dataset to the designer => dataSet11 appears.
Right-Click the existing DataGrid that is located in the Default.asax form. => Select Properties => DataSource => Set to dataSet11 => DataMember => TempPages => File => Save All.
Click on Build in the menubar. => Build Solution => Check for "Build: 1 succeeded, 0 failed, 0 skipped"
Run => Page shows up in Browser, but only the DataGrid's Titles show up, no DataGrid, even after making & saving Entries to the Members Table in the Database.

AnswerRe: No data shows from the TempPages Table?? What am I doing wrong??memberHoratiu CRISTEA30 Jan '03 - 22:20 
sorry for my late response.
 
when you run the application are you sure that there is any data in the Members table?
to populate the Members table you can execute the folowing sql script from Quary Analyzer:
 
declare 
	@i	int,
	@sufix	varchar
 
set @i = 1
 
while @i < 100
begin 
	set @sufix = convert(varchar,@i)  
 
	insert into Members( UserName, [Password], FullName, Email)
	values( 'UserName'+@sufix, 'Password'+@sufix, 'FullName'+@sufix, 'Email'+@sufix)
  
	set @i = @i + 1
end
 
this will populate the Members table exactly with the data i used for tests. if you experience more problems then write back.
 
i hope this helps
 
---------------
Horatiu CRISTEA
QuestionWhy store the record IDs?memberRichard_D28 Jul '02 - 23:13 
There's no real need for InitPages, you can just declare GetPage as:
 
CREATE PROCEDURE GetPage
    @PageNo       int,
    @RowsPerPage  int
AS
SET NOCOUNT ON
DECLARE
    @Start      int,
    @End        int,
    @Temp TABLE (
        RecordNo int NOT NULL IDENTITY(1,1),
        UserID int NOT NULL
    )
    
    INSERT INTO
        @Temp (UserID)
    SELECT
        UserID
    FROM
        Members
    ORDER BY
        UserID
    
    -- Calculate the boundaries between which I will select 
    SELECT
        @Start = (@PageNo - 1) * @RowsPerPage + 1,
        @End = @PageNo * @RowsPerPage
    
    -- Select all the records from Members and join TempPages
    SELECT
        Members.*
    FROM 
        @Temp As TP 
    INNER JOIN 
        Members 
    ON 
        TP.UserID = Members.UserID
    WHERE
        TP.RecordNo >= @Start 
    AND
        TP.RecordNo <= @End
    ORDER BY 
        RecordNo
GO
This works in SQL Server 2000. For SQL7, you can use the #Temp table.
You might see a slight decrease in performance, since you're not cacheing the user ids between calls, but you won't miss any that have been added or deleted, and you won't have to worry about cleaning the TempPages table.
AnswerRe: Why store the record IDs?memberHoratiu CRISTEA29 Jul '02 - 9:18 
It can be done like you said but the InitPages solution is prefered if you have a static database and a huge number of records tho the added records are not visible.
 
In your case, the records are initialized even when the user navigates between pages, you see the added records but it is a decrease in performance.
 
Its all about compromise Smile | :)
 

 
---------------
Horatiu CRISTEA
AnswerRe: Why store the record IDs?memberJATZ13 Feb '03 - 16:28 
There are drawbacks with both methods. I prefer the static method from a user perspective because it is how the data looked when the search was initially performed.
If searching each time the following may occur.
Example: I have looked at records 1 to 10, I click to see records 11 to 20. Assume that since my initial search someone has deleted record 3. So I get back the current records 11 to 20, but this is the old records 12 to 21. So I have missed out on seeing the old record 11 (or new record 10).
This could be made even more pronounced if multiple records were deleted, or even if the field that is being used for the sort order is changed.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 28 Jul 2002
Article Copyright 2002 by Horatiu CRISTEA
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid