Mimic LIMIT in MSSQL using ColdFusion QoQ

Community , MySQL Add comments

I use MySQL. I love MySQL. According to a great deal of feedback I've received re: Meld Forums being MySQL only, I seem to be in the minority (at least in the former). So, in making Meld Forums MS SQL-friendly, I made the startling, depressing discovery that MS SQL *doesn't* *have* *LIMIT* functionality. You can use TOP to get the first X rows of a query, but you cannot specify a start position.

This all comes down to the need for paging records in an application. In MySQL, getting a block of records is as easy as "LIMIT 50,50" to get records 51-100. In MS SQL the most common solution to this is to nest an identical child query within a parent query, with the aim of removing a subset of records from the returned results.

For example, say you have a list of products that you are searching. You only want to return 10 at a time, so in MySQL you would write the following query:

This will return the first 10 records, starting at record 0. LIMIT 10,10 will return records 11-20. In MS SQL, you cannot use TOP 10,10 ... it does not accept a start position as an argument. The standard way to replicate this functionality is as follows:

So, we are returning the first 20 records, minus the first 10 records. This looks pretty straight forward, but once you start throwing in ordering, groups, multiple joins etc. things get ugly and complicated very quickly. It also makes the process of development much more complicated, which is something I absolutely hate.

A very good balance between ease of development and performance is to employ Query of Queries to do the LIMIT replication. The process is as follows:

The process is that we a) select the block of 'skipped' records plus the block of 'returned' records from the database, then b) select the block of records that we don't want returned, and finally c) remove the unwanted records from our initial set by excluding them.  Technically we don't need to limit the return set in the final query as we should only have our block of returned rows left over, but I leave it in just in case the limit in the initial query was forgotten (it's also a good 'debug' technique because it ensures only the requested block of rows is returned, and any deviation from that will be instantly noticeable).

So, we have essentially duplicated the standard "exclude query within the query" methodology, but have saved ourselves a ton of development time by not having to replicate what might be a very complex query. The db engine is still doing the majority of the heavy lifting in our initial query, and for MS SQL we use QoQ's to limit our results.

Now, I'll be the first to admit that this isn't always going to be as performance-efficient as doing this all in the database query, but depending upon the complexity of the query this method can actually be faster! The performance improvement can come when you have a very complex query that includes sorting and grouping ... in these cases the standard 'nested' method in effect doubles the 'heavy' work your db server has to do (once in the outer query, and repeated again in the exclude sub-query). In the QoQ we are doing a simple exclusion on the primary key, which is fast and highly optimized. Balancing this method against the complexity of writing the nested queries, I take the minor performance hit as an acceptable balance against the loss of LIMIT functionality in MS SQL.

 

4 responses to “Mimic LIMIT in MSSQL using ColdFusion QoQ”

  1. Matthew Lesko Says:
    Starting with SQL Server 2005, you can use ROW_NUMBER() to achieve the same pagination as MySQL's LIMIT. For an example see:

    http://blogs.msdn.com/b/sqlserver/archive/2006/10/25/limit-in-sql-server.aspx

    Plus, the ROW_NUMBER() approach has the added benefit of not needing to return all rows to ColdFusion for paring down.

    Also, QofQ at least in ColdFusion 8, uses a single-threaded lock - at least on a per cfquery name="" basis - perhaps globally. This became a huge performance bottleneck for us under load.
  2. Grant Says:
    @Matthew some interesting points.

    First, I've looked at ROW_NUMBER() and IMHO the solution is only marginally better than the nested sub-query exclude method when dealing with sorted, grouped or joined-table queries, and in these cases the ROW_NUMBER() method still adds a significant amount of implementation code to make it work.

    As to QoQ being single-threaded, that's news to me and for the life if me I can't find any documentation that indicates this is so. Added to this I can't think of a reasonable answer *why* they would need to be single-threaded any more so than a regular query would. I'm not saying it isn't true, but I'd love to see some information that discusses/demonstrates this is the case.

    The fact is that the most optimized solution is always going to be the fastest, and the QoQ solution certainly isn't the answer for *every* situation. If you are working under heavy load and need every ounce of performance, or for some reason people are browsing a thousand pages into your data set, then writing db-optimized queries is the only way to go. If it is for something as simple as browsing a few dozen pages into a set of records, the performance differences using this solutions should be all but unnoticeable.
  3. Matthew Lesko Says:
    I agree, your solution works and requires less code. And, not trying to criticize your solution, just explaining some of the issues we've run into going down a similar path.

    Regarding QofQ single-threading: haven't confirmed with Adobe, but have seen in practice with more than one customer's error logs where the exception is inside the JDBC driver backing QofQ. My assumption is the QofQ JDBC driver relies on a table definition kept in memory, which it single threads to prevent changes to it during the course of a query. Could be wrong.

    ROW_NUMBER() and performance vs. QofQ: aside from the leading rows to pare down, there's also the size the columns - BLOBs/CLOBs in particular impact performance because the database server sends more bytes over the wire to ColdFusion.
  4. Colin Eyo Says:
    Note: QoQ does not support TOP function in the SQL. Easily solved by using MAXROWS attribute within the cfquery tag.

Leave a Reply

Leave this field empty:

Powered by Mango Blog. Design and Icons by N.Design Studio
Clicky Web Analytics