Home > ColdFusion, MySQL, Web Coding > Pagination with ColdFusion and MySQL

Pagination with ColdFusion and MySQL

September 24th, 2007

I find that web development with MySQL to be so much more pleasurable than with MS SQL Server. Here is a great example to prove my point. In another post I showed how to pull off pagination with MS SQL and ColdFusion. Lots of SQL code. And it required running the query twice. Once to count the entire result set, and another time for the results to display.
This version however, is very slim. It makes use of MySQL’s SQL_CALC_FOUND_ROWS, FOUND_ROWS(), and LIMIT.


SQL_CALC_FOUND_ROWS():

SQL_CALC_FOUND_ROWS and FOUND_ROWS() can be useful in situations when you want to restrict the number of rows that a query returns, but also determine the number of rows in the full result set without running the query again. An example is a Web script that presents a paged display containing links to the pages that show other sections of a search result. Using FOUND_ROWS() allows you to determine how many other pages are needed for the rest of the result.

FOUND_ROWS():

A SELECT statement may include a LIMIT clause to restrict the number of rows the server returns to the client. In some cases, it is desirable to know how many rows the statement would have returned without the LIMIT, but without running the statement again. To obtain this row count, include a SQL_CALC_FOUND_ROWS option in the SELECTFOUND_ROWS() afterward.

LIMIT:

The LIMIT clause can be used to constrain the number of rows returned by the SELECT statement. LIMIT takes one or two numeric arguments. With two arguments, the first argument specifies the offset of the first row to return, and the second specifies the maximum number of rows to return.

So let’s go take a look at this beauty…

<!--- CURRENT PAGE --->
<cfparam name="url.page" default="1">
<!--- RECORDS PER PAGE (ON ALL PAGES) --->
<cfparam name="variables.allperpage" default="6">
<!--- RECORDS PER PAGE (ON FIRST PAGE) --->
<cfparam name="variables.firstperpage" default="3">
<!--- NUMBER OF PAGE LINKS AT A TIME --->
<cfparam name="variables.blocksof" default="4">

<!--- WE RUN THE QUERY WITH SQL_CALC_FOUND_ROWS --->
<cfquery name="latest" datasource="MagLibrary">
SELECT SQL_CALC_FOUND_ROWS articles.title, articles.articleid,
	publications.title as pubtitle, publications.rank
FROM articles INNER JOIN publications ON
	articles.publicationID = publications.publicationID
WHERE articles.active = 1
ORDER BY publications.rank, pubtitle, articles.title, articles.articleid
<!--- ONLY RETURN THE AMOUNT OF RECORDS WE NEED FOR THIS PAGE --->
LIMIT #firstperpage+(allperpage*(page-1))-allperpage#,
<cfif url.page is 1>
	#firstperpage#
<cfelse>
	#allperpage#
</cfif>
</cfquery>

<!--- NOW WE QUERY HOW MANY ROWS WERE FOUND --->
<Cfquery name="articlecount" datasource="#session.datasource#">
SELECT FOUND_ROWS() AS articlecount
</cfquery>

<!--- 'TOP' IS THE LAST PAGE NUMBER LINK --->
<Cfset top = blocksof * ceiling((url.page)/blocksof)>

<cfoutput>

<!--- IF WE'RE PAST THE FIRST BLOCK OF PAGES, SHOW THE PREV LINK --->
<cfif url.page gt blocksof>
	<a href="?page=#top-blocksof#" class="fullstorylink" style="text-decoration:underline;">« Prev</a>
</cfif>

<!--- LOOP THROUGH THE PAGES IN THIS BLOCK --->
<cfloop from="#top-blocksof+1#" to="#top#" index="a">
<cfif url.page is not a>
	<a href="?page=#a#" class="fullstorylink" style="text-decoration:underline;">#a#</a>
<Cfelse>
	<b>[#a#]</b>
</cfif>
<!--- IF WE'RE ON THE LAST BLOCK AND DON'T HAVE ENOUGH RECORDS TO COMPLETE THE BLOCK,
	STOP CREATING THE LINKS FOR MORE PAGES, AND SET A FLAG TO NOT SHOW THE LINK FOR THE NEXT BLOCK --->
<cfif ((a-1) * allperpage) + firstperpage gte articlecount.articlecount>
	<Cfset noNext=1><cfbreak>
</cfif>
</cfloop>

<!--- IF WE RAN OUT OF RECORDS, DO NOT SHOW THE LINK FOR THE NEXT BLOCK OF PAGES --->
<cfif not isdefined("noNext")>
	<a href="?page=#top+1#" class="fullstorylink" style="text-decoration:underline;">Next »</a>
</cfif>

</cfoutput>

<!--- NOW IT'S A SIMPLE MATTER OF SHOWING THE RECORDS --->
<br><br>

<!--- WE'LL GROUP THE ARTICLES UNDER THEIR PUBLICATION --->
<cfoutput query="latest" group="pubtitle">
<h2>#pubtitle#</h2>
<cfoutput>
<a href="details.cfm?articleid=#articleid#">#title#</a>
</cfoutput>
</cfoutput>

For those of you counting lines, we went from 94 (MS SQL) down to 70 (MySQL). That’s 24 less lines of SQL code. Not too shabby. And just knowing your programming is more efficient is a good feeling =)

ColdFusion, MySQL, Web Coding

  1. April 8th, 2008 at 14:10 | #1

    Hello, thanks for this great tutorial.
    Just a question: where do you get the database from?
    Just wondering. Thanks

  2. ruth
    May 5th, 2008 at 16:01 | #2

    This logic does not work by me . Do i need to use #session.datasource#
    It looks like the select found_rows() gives me only 45 records all the time

  3. April 8th, 2008 at 19:15 | #3

    You’re welcome!
    The database is fictional. There would be one table of Publications and another of Articles.

  4. May 5th, 2008 at 19:09 | #4

    No, you can use your own datasource name or variable. select found_rows() will always give you the same number, every time. That’s sorta the point :)

  5. ruth
    May 6th, 2008 at 08:28 | #5

    I am sorry I do not understand, why found_rows () does not work in cold fustion.
    I am using the logic with many query even thought the records count should be like 2000 records, the select found_rows () gives me 45 all the time.

    <cfquery name="sectContent" datasource="#DB#" cachedwithin="#CreateTimeSpan(0, 1, 0, 0)#">
    SELECT SQL_CALC_FOUND_ROWS distinct(c.ContentID), ContentTitle, ContentDesc, imageSmall,imageMore, contentDate, ContentField1
    FROM content c INNER JOIN ContentTocontent ctc ON c.ContentID = ctc.ParentID
    WHERE
    ctc.ContentID in ( #concatList# )
    LIMIT #start – 1#,#perPage#
    </cfquery>

    <cfquery name="contentCount" datasource="#DB#" cachedwithin="#CreateTimeSpan(0, 1, 0, 0)#">
    select found_rows() as num
    </cfquery>

  6. May 6th, 2008 at 13:30 | #6

    It may be DISTINCT that is throwing off the count. Can you verify that there are only (or more) 45 distinct c.ContentID ?

  7. July 17th, 2008 at 05:48 | #7

    @ruh,

    I ran into the same problem recently. The script that I had working was working fine for quite sometime and than all of a sudden it stopped for some reason. Unfortunately, there isn’t a fix to get it to work again, but there is an alternate solution that will help that I’ve actually put to work and it works fine. First, remove the SELECT SQL_CALC_FOUND_ROWS from your 1st query, you don’t need it at all for the new solution. Next, you want to change the 2nd query from SELECT FOUND_ROWS() AS num to SELECT COUNT(0) AS num FROM whatever_table. Believe me when I tell you that this is lightening fast, and possibly faster than the FOUND_ROWS() query, I tested it on a table with 1.4 million records and it is extremely fast, no hesitation whatsoever.

    FYI for anyone interested, the reason his query returned 45 vs. returning the number of records is that for some crazy reason Coldfusion ends up returning the number of columns rather than the number of rows. I spent many hours researching and trying all sorts of Connector/J and none of them worked. I tried all the updaters for JRun and no luck either. I think that it just stops working without a fix, but this should do the trick!

  8. June 26th, 2008 at 05:30 | #8

    Out of the box, your code is defective and i’m not doing this to sound insulting.

    First of all, in the first cfquery after the SQL_CALC_FOUND_ROWS there shouldn’t be a comma. I checked the docs and I also ran a test on my local database.

    Second, the LIMIT #firstperpage+(allperpage*(page-1))-allperpage# gave me a -3 when I ran it and I tried it on several databases, not sure how you managed to get this to work.

    Please re-run this on your database, or email me the code and the sql dump so that I can mimic what you are doing to verify. I could be wrong, but like I said, I tried this on several databases and none of it worked.

    Has anyone else had the same problem?

  9. August 3rd, 2008 at 14:56 | #9

    There is a newer version to this, with fixes and enhancements. Please check out:
    http://www.webveteran.com/blog/index.php/coldfusion/mysql-and-coldfusion-pagination-version-2/

  1. No trackbacks yet.