ColdFusion CFQuery MaxRows Not A Bad Compromise When TOP Not Available

Posted January 8, 2007 at 7:53 PM by Ben Nadel

Tags: ColdFusion, SQL

Not so long ago, I was introduced to the MaxRows attribute of the ColdFusion CFQuery tag (thanks Joe Rinehart). It's one of those things that you can't believe you didn't know about, but sure enough, there it is. If anyone else doesn't know what it does, it limits the number of records that can be returned by a given query. So, for instance, the following query:

  • <!--- Limit the record set using MaxRows. --->
  • <cfquery name="qMovie" datasource="..." maxrows="30">
  • SELECT
  • *
  • FROM
  • movie
  • ORDER BY
  • name ASC
  • </cfquery>

... will only return the TOP 30 rows to the ColdFusion memory space since it's MaxRows attribute is set to "30." This is similar to the way the TOP directive works in SQL server:

  • <!--- Limit the record set using TOP. --->
  • <cfquery name="qMovie" datasource="...">
  • SELECT TOP 30
  • *
  • FROM
  • movie
  • ORDER BY
  • name ASC
  • </cfquery>

Both the above queries accomplish the same thing by limiting the max number of returned records to 30. However, one is a ColdFusion directive (MaxRows) and one is a SQL directive (TOP). When compared though, MaxRows does pretty well. When I "select *" from a database that has 50,000+ records, using the TOP directive executed in about 30-40 ms. MaxRows returned the same record set in around 125-140 ms. This is about 3-4 times slower... but when you compare that to the query that ran without either the TOP or the MaxRows directive (on average 400-450 ms), it's a pretty darn good compromise when the SQL TOP directive is not available.

I don't know why MaxRows executes faster than the query that has neither TOP nor MaxRows. I assume that ColdFusion is basically cutting off the database stream once the "MaxRows" number of records have been returned (but this is just theory). I wonder how this affects the SQL Server. I wonder if, from the SQL standpoint, it is still returning all those rows? Does the SQL server still have to amass the massive record set?

Efficiency aside, MaxRows is sometimes the only option available, such as when performing a ColdFusion query of queries (which is where Joe jumped in and pointed out my ignorances).



Reader Comments

Jan 8, 2007 at 8:56 PM // reply »
153 Comments

I know that OLEDB, and I think also ODBC, can tell the SQL Server that it only wants the first n rows, and I know that the SQL Server will optimize for that. (I think I remember reading something about SQL Server even being smart enough to throw away unneeded data mid-query once it has fulfilled it's minimum result set.)

And, of course, always remember that Postgres and mySQL use the LIMIT n syntax, DB2 uses the FETCH FIRST n ROWS ONLY syntax, and Oracle uses ROWNUM in the WHERE clause.

PITA, eh?


Jan 9, 2007 at 7:32 AM // reply »
11,238 Comments

What? They couldn't come up with a way to make "FETCH FIRST n ROWS ONLY" even longer? Maybe throw a "please" at the end :)

Rick, thanks for the insight. Good to know that optimization does occur and that it occurs in both the ColdFusion server and the SQL server (potentially).


Jan 9, 2007 at 7:53 AM // reply »
6 Comments

Here is how I would do it in MySQL

The First 10
Select * from table order by ID Limit 10

The Last 10

Select * from table order by ID Desc Limit 10


Jan 9, 2007 at 11:08 AM // reply »
153 Comments

Actually, it's even worse than that. For most queries via CF, it's a good idea to tell the DB2 engine that the query you are about to run is not going to be updated. It will avoid some locking problems and speed up your query by around 5%. In SQL server, it's pretty straightforward:

SELECT TOP 10 foo, bar
FROM mytable (NOLOCK)

How do you do it in DB2?

SELECT foo, bar
FROM mylib.mytable
FETCH FIRST 10 ROWS ONLY
FOR FETCH ONLY

Someone at IBM really, really liked the word "ONLY".


Jan 9, 2007 at 12:07 PM // reply »
11,238 Comments

@Rick,

That is really interesting. I have never seen that before. Is that something you should do only if you are not expecting simultaneous hits to the DB? Or is that something you can do any time you are just reading?


Jan 9, 2007 at 1:09 PM // reply »
153 Comments

For NOLOCK, it allows your query to perform dirty reads, which may or may not be applicable to your situation. For data that changes very infrequently but is queried often (especially as part of a large join), dirty reads may improve performance by reducing table contention.

But!

Dirty reads can also read data that never actually gets committed! If a transaction fails and is rolled back, a dirty read might get the bogus data. I only use it for lookup tables that almost never change.

Done completely in DB2:

SELECT foo, bar
FROM mylib.mytable
FETCH FIRST 10 ROWS ONLY
OPTIMIZE FOR 10 ROWS
FOR FETCH ONLY
WITH UR

That "optimize" part kindof acts like the server-size equivalent of CFQuery's BLOCKFACTOR attribute, while the "UR" part represents "uncommitted read", the dirty part.

DB2 is nothing if not verbose. It may seem like over-optimization, but it's good to know how to do just in case.


Aug 13, 2009 at 10:16 AM // reply »
1 Comments

Here's a tidbit to make maxrows more useful. I had a cfc method that sometimes I wanted to return all records, sometimes top two or three. No problem, pass in a 2 or 3 as an argument and drop it in maxrows. So far so good. But what if I want to see all records? It turns out that passing in -1 works.


Aug 18, 2009 at 6:32 PM // reply »
11,238 Comments

@Jim,

Oh cool, didn't know that.


Oct 11, 2009 at 8:13 AM // reply »
1 Comments

I just found this post:
http://www.coldfusionjedi.com/index.cfm/2009/8/12/MAXROWS-Attribute--Not-as-good-as-I-thought

It is worth reading if you are trying to use MAXROWS to improve performance. It probably is not doing what you expect.


Jan 20, 2010 at 9:21 PM // reply »
9 Comments

Beware using the maxrows with the SQL Statement WHERE IN () Because it will give the query rows result as many as you define it in the maxrows. Instead of million records you expected, it will return only X records. This problem might come from the Coldfusion SQL Server driver.


Jan 20, 2010 at 10:28 PM // reply »
11,238 Comments

@Jusuf,

I am not sure what you are saying? The use of MaxRows is designed to limit the number of records reported in the query? What are you saying about how it interacts with IN()?


Jan 20, 2010 at 10:59 PM // reply »
9 Comments

Let's say you query 1M rows with maxrows=10. When you dump the recordresult it will return 1M however if you have the SQL Statement using subquery IN for some reasons it returns 10 instead of 1M.

One of the most interesting with Maxrows is you can loop from 1-1M using startrow (1,11,21,..) and endrow (10,20,30,...) and get 10 records per page.


Jan 21, 2010 at 2:06 PM // reply »
9 Comments

@Ben Nadel, So sorry I think I got mess up here. Just forgot/delete my previous comments. What I find out is when I used the SELECT TOP n only getting the record result from 1-n however the maxrows it's selecting n records from the result. This will make different for example you want to sort the order by date descending. The TOP n only sort on the top n records.

Again, sorry on my previous records. Thanks.


Jan 21, 2010 at 2:09 PM // reply »
11,238 Comments

@Jusuf,

No worries at all - that's what we're here for - good conversation, no matter where it leads.


Dec 6, 2011 at 2:10 AM // reply »
1 Comments

Suppose I have a cfquery that has maxrows attribute set to 50. What do i need to do in order to get the 51st row using the same cfquery without changing the maxrows attribute value?

Thanks.


Dec 10, 2011 at 4:02 PM // reply »
13 Comments

@Ben,

Thanks for doing the legwork on this... It's good enough for me to presume that MySQL's LIMIT is somewhere on par with TOP over maxrows.

@Sharath,

With MySQL you can forget about maxrows and do it like this:

SELECT column FROM table LIMIT 1 OFFSET 50

Other than that, you could set maxrows to 51 and extract the data you require like so:

  • <cfloop query="myQuery">
  • <cfif myQuery.currentrow eq 51>
  • DO SOMETHING
  • </cfif>
  • </cfloop>



Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 19, 2013 at 2:31 PM
My Experience With AngularJS - The Super-heroic JavaScript MVW Framework
It's funny really just how well that image describes the way I would imagine most people that go with angular for some project is. I have had a similar roller-coaster ride with it as well, but not qu ... read »
May 17, 2013 at 7:42 PM
HashKeyCopier - An AngularJS Utility Class For Merging Cached And Live Data
Ben - thanks so much for posting these Angular articles and findings, they've been a huge help towards learning one of the more 'complex' JavaScript frameworks out there (IMO). I have been using Angu ... read »
May 16, 2013 at 5:01 PM
UPDATE: Parsing CSV Data Files In ColdFusion With csvToArray()
Your code was the closest thing I've found to obtaining some direction for converting ISO fields to values that CF can translate properly. Thank you for posting! ... read »
May 15, 2013 at 10:37 PM
Very Simple Pusher And ColdFusion Powered Chat
hi id making plz easy ... read »
May 15, 2013 at 6:07 PM
Making SOAP Web Service Requests With ColdFusion And CFHTTP
Ben, you once again saved my bacon at work. Thank you, thank you, thank you! ... read »
May 15, 2013 at 4:15 PM
What If All User Interface (UI) Data Came In Reports?
@Josh, Thanks! @Ben, I definitely recommend the David West book "Object Thinking" I've been quoting from. It goes deeply into the philosophy and history of OO programming. His breadth ... read »
May 15, 2013 at 11:36 AM
Ask Ben: Print Part Of A Web Page With jQuery
I found this helpfull when you need to keep (refresh) the original parent page after closing the iframe child print dialog (Hoping you're not using a form at this time so it won't submit again): On ... read »
May 14, 2013 at 7:13 PM
What If All User Interface (UI) Data Came In Reports?
@Jonah, If there's any books you'd recommend on the subject of domain modelling, I'd love to hear it. I just downloaded the free PDF of "Domain Driven Design Quickly". Figured I'd give it ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools