//  home   //  advanced search   //  news   //  categories   //  sql build chart   //  downloads   //  statistics
 ASP FAQ 
Home
ASP FAQ Tutorials

   8000XXXX Errors
   Alerts
   ASP.NET 2.0
   Classic ASP 1.0
   Databases
      Access DB & ADO
      General SQL Server & Access Articles
      MySQL
      Other Articles
      Schema Tutorials
      Sql Server 2000
      Sql Server 2005
   General Concepts
   Search Engine Optimization (SEO)

Contact Us
Site Map

Search

Web
aspfaq.com
tutorials.aspfaq.com
databases.aspfaq.com

ASP FAQ Tutorials :: Databases :: Other Articles :: How do I handle alphabetic paging?


How do I handle alphabetic paging?

The following will allow you to query a table for last names beginning with each letter in the alphabet. 
 
<% 
    Ltr = UCase(Request.QueryString("Ltr")) 
    if (Ltr < "A") or (Ltr > "Z") then Ltr = "A" 
    for i = 65 to 90 
        if i > 65 then response.write " | " 
        if Asc(Ltr) <> i then 
            response.write "<a href='Alpha.asp?Ltr=" & _ 
                Chr(i) & "'>" & Chr(i) & "</a>" 
        else 
            response.write "<b>" & Chr(i) & "</b>" 
        end if 
    next 
    SQL = "SELECT lastName FROM nameTable WHERE lastName LIKE '" & Ltr & "%'" 
    Response.Write "<p>" & SQL 
 
    ' ... execute query and process results 
%>
 
Now, you might only be interested in letters (and other characters) that are actually represented in your database. Most of you probably don't have last names that start with Q or X, for example; and sometimes a user will enter their name incorrectly, e.g. !Smith. So you could consider building your alphabet list like this: 
 
<% 
    Ltr = UCase(Request.QueryString("Ltr")) 
    Set Conn = CreateObject("ADODB.Connection") 
    conn.open "<connectionString>"      
    SQL = "SELECT DISTINCT LEFT(lastName,1) FROM nameTable ORDER BY LEFT(lastName,1)" 
    set rs = conn.execute(SQL) 
    do while not rs.eof 
        response.write " | " 
        rs0 = UCase(rs(0)) 
        if rs0 <> Ltr then 
            response.write "<a href='Alpha.asp?Ltr=" & _ 
                rs0 & "'>" & rs0 & "</a>" 
        else 
            response.write "<b>" & rs0 & "</b>" 
        end if 
        rs.movenext 
    loop 
    response.write " |" 
 
    SQL = "SELECT lastName FROM nameTable WHERE lastName LIKE '" & Ltr & "%'" 
    Response.Write "<p>" & SQL 
 
    ' ... execute query and process results 
%>
 

 
Something More Challenging 
 
Another request has been for code that shows the divisions between the pages, so that, for example, if I have 300 names that start with A, and only 100 that start with B, and so on, there wouldn't be 300 results on page 1, and 100 results on page 2. Rather, they would be grouped in divisions of some pre-defined number, and labeled/linked like this: 
 
 
    Aardvark - Abigail 
    AbyNormal - Anderson 
    Andersson - Anderwurst 
    Andres - Atwell 
    ...
 
And you would click on the first link, and see all the names between Aardvark and Abigail. When you click on the next link, you would see the same number of names, but they would be from AbyNormal to Anderson with one s, and so on. Well, here is an example using SQL Server that will let you do this. Note that I have an IDENTITY column in order to provide a primary key (even though it's a surrogate), and I added a computed column for e-mail address, just to return some extraneous data with the query. 
 
 
SET NOCOUNT ON 
 
CREATE TABLE FaqUsers 

    FirstName VARCHAR(32) NOT NULL, 
    LastName VARCHAR(32) NOT NULL 

GO 
 
CREATE UNIQUE CLUSTERED INDEX lnfn 
    ON FaqUsers(LastName, FirstName) 
GO 
 
INSERT FaqUsers(LastName, FirstName) VALUES('Aardvark', 'Bob') 
INSERT FaqUsers(LastName, FirstName) VALUES('Abbott', 'Bob') 
INSERT FaqUsers(LastName, FirstName) VALUES('Barnsworth', 'Bill') 
INSERT FaqUsers(LastName, FirstName) VALUES('Bertrand', 'Aaron') 
INSERT FaqUsers(LastName, FirstName) VALUES('Burns', 'Edward') 
INSERT FaqUsers(LastName, FirstName) VALUES('Burns', 'Mr.') 
INSERT FaqUsers(LastName, FirstName) VALUES('Clamshell', 'Grill') 
INSERT FaqUsers(LastName, FirstName) VALUES('Casper', 'Ghosty') 
INSERT FaqUsers(LastName, FirstName) VALUES('Clatton', 'Amy') 
INSERT FaqUsers(LastName, FirstName) VALUES('Da Vinci', 'Leonardo') 
INSERT FaqUsers(LastName, FirstName) VALUES('Dafoe', 'Byron') 
INSERT FaqUsers(LastName, FirstName) VALUES('Damaso', 'Tommy') 
INSERT FaqUsers(LastName, FirstName) VALUES('Damaso', 'Virgil') 
INSERT FaqUsers(LastName, FirstName) VALUES('Duke', 'Bo') 
INSERT FaqUsers(LastName, FirstName) VALUES('Duke', 'Daisy') 
INSERT FaqUsers(LastName, FirstName) VALUES('Duke', 'Luke') 
INSERT FaqUsers(LastName, FirstName) VALUES('Ertl', 'Ham') 
INSERT FaqUsers(LastName, FirstName) VALUES('Finch', 'Dundas') 
INSERT FaqUsers(LastName, FirstName) VALUES('Grep', 'Rtfm') 
INSERT FaqUsers(LastName, FirstName) VALUES('Hatcher', 'Derian') 
INSERT FaqUsers(LastName, FirstName) VALUES('Hazard', 'Duke') 
INSERT FaqUsers(LastName, FirstName) VALUES('Isaac', 'Newton') 
INSERT FaqUsers(LastName, FirstName) VALUES('Islington', 'Jon') 
INSERT FaqUsers(LastName, FirstName) VALUES('Johnson', 'John') 
INSERT FaqUsers(LastName, FirstName) VALUES('Johnston', 'John') 
INSERT FaqUsers(LastName, FirstName) VALUES('Klemp', 'Stooge') 
INSERT FaqUsers(LastName, FirstName) VALUES('Laderoute', 'Jamie') 
INSERT FaqUsers(LastName, FirstName) VALUES('McBain', 'McDuff') 
INSERT FaqUsers(LastName, FirstName) VALUES('McLaren', 'Klye') 
INSERT FaqUsers(LastName, FirstName) VALUES('Nincombe', 'Oop') 
INSERT FaqUsers(LastName, FirstName) VALUES('Osprey', 'Ray') 
INSERT FaqUsers(LastName, FirstName) VALUES('Petrovic', 'Ivan') 
INSERT FaqUsers(LastName, FirstName) VALUES('Russell', 'Leon') 
INSERT FaqUsers(LastName, FirstName) VALUES('Simpson', 'Homer') 
INSERT FaqUsers(LastName, FirstName) VALUES('Sizlack', 'Moe') 
INSERT FaqUsers(LastName, FirstName) VALUES('Sizzlean', 'Bacon') 
INSERT FaqUsers(LastName, FirstName) VALUES('Thomsen', 'Inge') 
INSERT FaqUsers(LastName, FirstName) VALUES('Thomson', 'Ted') 
INSERT FaqUsers(LastName, FirstName) VALUES('Thompson', 'Mona') 
INSERT FaqUsers(LastName, FirstName) VALUES('Trascal', 'Ronnie') 
INSERT FaqUsers(LastName, FirstName) VALUES('Udelay', 'Heyhoo') 
INSERT FaqUsers(LastName, FirstName) VALUES('VanWyck', 'Lisa') 
INSERT FaqUsers(LastName, FirstName) VALUES('Washington', 'Malivai') 
INSERT FaqUsers(LastName, FirstName) VALUES('Weatherspoon', 'Dumb') 
INSERT FaqUsers(LastName, FirstName) VALUES('Witheringham', 'Hiriam') 
GO
 
So, now we have some sample data. Let's say we want to show 10 of these names per page, and show nice pretty links as described above. 
 
Here is a stored procedure that will produce two recordsets; the first contains the HTML that produces the links to the different divisions, and the second produces the 10 names for the current set. 
 
CREATE PROCEDURE dbo.showNames 
    @divID INT = 1 
AS 
BEGIN 
    SET NOCOUNT ON 
 
    CREATE TABLE #nameDivs 
    ( 
        divID INT IDENTITY(1,1), 
        f VARCHAR(32) NOT NULL, 
        l VARCHAR(32) NOT NULL, 
        link BIT NOT NULL DEFAULT 0 
    ) 
 
    WHILE 1 = 1 
    BEGIN 
        IF EXISTS (SELECT 1 FROM #nameDivs) 
        BEGIN 
            INSERT #nameDivs(f, l, link) 
            SELECT 
                MIN(LastName), 
                MAX(LastName), 
                link = CASE 
                WHEN @divID =  
                ( 
                    SELECT MAX(divID)+1 
                    FROM #nameDivs 
                ) THEN 0 ELSE 1 END 
            FROM 
            ( 
                SELECT TOP 10 LastName 
                    FROM FaqUsers f 
                    WHERE LastName >  
                    ( 
                        SELECT MAX(l) 
                        FROM #nameDivs 
                    ) 
                    ORDER BY LastName 
            ) x 
        END 
        ELSE 
        BEGIN 
            INSERT #nameDivs(f, l, link) 
            SELECT 
                MIN(LastName), 
                MAX(LastName), 
                link = CASE WHEN @divID = 1 
                THEN 0 ELSE 1 END 
            FROM 
            ( 
                SELECT TOP 10 LastName 
                    FROM FaqUsers f 
                    ORDER BY LastName 
            ) x 
        END 
 
        IF EXISTS  
        ( 
            SELECT 1 FROM #nameDivs 
            WHERE l =  
            ( 
                SELECT MAX(lastName) 
                    FROM FaqUsers 
            ) 
        ) 
        BEGIN 
            BREAK 
        END 
    END 
 
    DECLARE @maxl VARCHAR(32), @minl VARCHAR(32), @maxDiv INT 
 
    SELECT 
        @minl = MIN(f), 
        @maxl = MAX(l), 
        @maxDiv = MAX(DivID) 
        FROM #nameDivs 
 
    SELECT [html] =  
        CASE WHEN f = @minl THEN '[' ELSE '|' END 
        + ' ' + CASE WHEN link = 1 THEN 
        '<a href=shownames.asp?divID='+RTRIM(DivID)+'>' 
        + f + '...' + l + '</a>' 
        ELSE '<b>' + f + '...' + l + '</b>' END 
        + ' ' + CASE WHEN l = @maxl THEN ']' 
        ELSE '' END 
        FROM #nameDivs ORDER BY DivID 
 
    DECLARE @fLN VARCHAR(32), @lLN VARCHAR(32) 
 
    SELECT @divID = CASE  
        WHEN @divID > @maxDiv THEN @maxDiv 
        ELSE @divID END 
 
    SELECT 
        @fLN = f, 
        @lLN = l 
        FROM #nameDivs 
        WHERE DivID = @divID 
 
    SELECT 
        FirstName, 
        LastName 
        FROM FaqUsers 
        WHERE LastName BETWEEN @fLN AND @lLN 
        ORDER BY LastName, FirstName 
 
    DROP TABLE #nameDivs 
END 
GO
 
So from shownames.asp, you would call it like this: 
 
<% 
    divID = request.querystring("divID") 
    if not isnumeric(divID) then divID = 1 
    sql = "EXEC dbo.showNames " & divID 
    set conn = CreateObject("ADODB.Connection") 
    conn.open "<connection string>" 
    set rs = conn.execute(sql) 
    do while not rs.eof 
        response.write rs(0) 
        rs.movenext 
    loop 
    response.write "<hr>" 
    set rs = rs.nextrecordset() 
    do while not rs.eof 
        response.write rs("FirstName") & " " 
        response.write rs("LastName") & "<br>" 
        rs.movenext 
    loop 
    rs.close 
    set rs = nothing 
    conn.close 
    set conn = nothing      
%>
 
And the results would look something like this:
 
And another request from Laphan 
 
Laphan wanted a combination of the above, where all letters were shown (regardless of whether they had any actual data). Those that had data would be linked, and those that did not, would not. 
 
First, create your names table: 
 
CREATE TABLE Names 

    name VARCHAR(32) 
)
 
Insert some dummy data: 
 
INSERT Names VALUES('33r') 
INSERT Names VALUES('9994') 
INSERT Names VALUES('Aaron') 
INSERT Names VALUES('Bob') 
INSERT Names VALUES('Dilbert') 
INSERT Names VALUES('Frank') 
INSERT Names VALUES('Foobar') 
INSERT Names VALUES('Mojo') 
INSERT Names VALUES('Zachary')
 
Now create an alphabet table to left join against: 
 
CREATE TABLE Alphabet 

    letter CHAR(1) 
)
 
And insert the 26 letters (not all included, for brevity): 
 
INSERT Alphabet VALUES('A') 
INSERT Alphabet VALUES('B') 
INSERT Alphabet VALUES('C') 
... 
INSERT Alphabet VALUES('X') 
INSERT Alphabet VALUES('Y') 
INSERT Alphabet VALUES('Z')
 
Now, to get the numerics, you need this query: 
 
<% 
    ' note: for Access, change = 1 to = TRUE 
    sql1 = "SELECT COUNT(*) FROM table WHERE ISNUMERIC(LEFT(column, 1))=1" 
    set rs = conn.execute(sql1) 
    if rs(0) > 0 then 
        response.write "<a href=page.asp?letter=0-9>0-9</a>" 
    else 
        response.write "0-9" 
    end if 
%>
 
And to get the letters, you can use a LEFT JOIN: 
 
<% 
    sql2 = "SELECT n = MIN(N.NAME), a.letter FROM alphabet a" & _ 
        "LEFT JOIN names n ON LEFT(n.name,1) = a.letter" & _ 
        "GROUP BY a.letter ORDER BY a.letter" 
 
    set rs = conn.execute(sql2) 
    do while not rs2.eof 
        if isnull(rs("n")) then 
            response.write rs("letter") 
        else 
            response.write "<a href=page.asp?letter=" 
            response.write rs("letter") & ">" & rs("letter") & "</a>" 
        end if 
        rs.movenext 
    loop 
%>
 
Yes, I would handle the above in a stored procedure. However I left it this way to illustrate that you could handle this very similarly if you are stuck using Access, like Laphan (seemingly, the only person interested in this article <G>). 
 
Let us know if you have any questions about any of these techniques.

Related Articles

How do I build a query with optional parameters?
How do I calculate the median in a table?
How do I create a store locator feature?
How do I deal with MEMO, TEXT, HYPERLINK, and CURRENCY columns?
How do I deal with multiple resultsets from a stored procedure?
How do I debug my SQL statements?
How do I determine if a column exists in a given table?
How do I enable or disable connection pooling?
How do I enumerate through the DSNs on a machine?
How do I find a stored procedure containing <text>?
How do I get a list of Access tables and their row counts?
How do I get the latest version of the JET OLEDB drivers?
How do I handle BIT / BOOLEAN columns?
How do I handle error checking in a stored procedure?
How do I ignore common words in a search?
How do I page through a recordset?
How do I present one-to-many relationships in my ASP page?
How do I prevent duplicates in a table?
How do I prevent my ASP pages from waiting for backend activity?
How do I prevent NULLs in my database from mucking up my HTML?
How do I protect my Access database (MDB file)?
How do I protect my stored procedure code?
How do I protect myself against the W32.Slammer worm?
How do I remove duplicates from a table?
How do I rename a column?
How do I retrieve a random record?
How do I return row numbers with my query?
How do I send a database query to a text file?
How do I simulate an array inside a stored procedure?
How do I solve 'Could not find installable ISAM' errors?
How do I solve 'Operation must use an updateable query' errors?
How do I temporarily disable a trigger?
How do I use a SELECT list alias in the WHERE or GROUP BY clause?
How do I use a variable in an ORDER BY clause?
Should I index my database table(s), and if so, how?
Should I store images in the database or the filesystem?
Should I use a #temp table or a @table variable?
Should I use a view, a stored procedure, or a user-defined function?
Should I use recordset iteration, or GetRows(), or GetString()?
What are all these dt_ stored procedures, and can I remove them?
What are the limitations of MS Access?
What are the limitations of MSDE?
What are the valid styles for converting datetime to string?
What datatype should I use for my character-based database columns?
What datatype should I use for numeric columns?
What does "ambiguous column name" mean?
What is this 'Multiple-step OLE DB' error?
What is wrong with 'SELECT *'?
What naming convention should I use in my database?
What should I choose for my primary key?
What should my connection string look like?
When should I use CreateObject to create my recordset objects?
Where can I get this 'Books Online' documentation?
Where do I get MSDE?
Which database platform should I use for my ASP application?
Which tool should I use: Enterprise Manager or Query Analyzer?
Why are there gaps in my IDENTITY / AUTOINCREMENT column?
Why can I not 'open a database created with a previous version...'?
Why can't I access a database or text file on another server?
Why can't I use the TOP keyword?
Why do I get 'Argument data type text is invalid for argument [...]'?
Why do I get 'Not enough space on temporary disk' errors?
Why does ASP give me ActiveX errors when connecting to a database?
Should I use COALESCE() or ISNULL()?
Where can I get basic info about using stored procedures?

 

 


Created: 8/10/2002 | Last Updated: 3/24/2005 | broken links | helpful | not helpful | statistics
© Copyright 2006, UBR, Inc. All Rights Reserved. (146)

 

Copyright 1999-2006, All rights reserved.
Finding content
Finding content.  An error has occured...