views:

2236

answers:

4

Hi all,

I was wondering if there was a way to get the number of results from a MySQL query, and at the same time limit the results.

The way pagination works (as I understand it), first I do something like

query = SELECT COUNT(*) FROM `table` WHERE `some_condition`

After I get the num_rows(query), I have the number of results. But then to actually limit my results, I have to do a second query like:

query2 = SELECT COUNT(*) FROM `table` WHERE `some_condition` LIMIT 0, 10

My question: Is there anyway to both retrieve the total number of results that would be given, AND limit the results returned in a single query? Or any more efficient way of doing this. Thanks!

+2  A: 
query = SELECT col, col2, (SELECT COUNT(*) FROM `table`) AS total FROM `table` WHERE `some_condition` LIMIT 0, 10
Cris McLaughlin
This query just returns the total number of record in the table; not the number of records that match the condition.
Lawrence Barsanti
+7  A: 

No, that's how many applications that want to paginate have to do it. It's reliable and bullet-proof, albeit it makes the query twice. But you can cache the count for a few seconds and that will help a lot.

The other way is to use SQL_CALC_FOUND_ROWS clause and then call SELECT FOUND_ROWS(). apart from the fact you have to put the FOUND_ROWS() call afterwards, there is a problem with this: There is a bug in MySQL that this tickles that affects ORDER BY queries making it much slower on large tables than the naive approach of two queries.

staticsan
Excellent, thanks for your help!
Jasie
It's not quite race-condition proof, however, unless you do the two queries within a transaction. This generally isn't a problem, though.
Sharkey
By "reliable" I meant the SQL itself is always going to return the result you want, and by "bullet-proof" I meant that there are no MySQL bugs hampering what SQL you can use. Unlike using SQL_CALC_FOUND_ROWS with ORDER BY and LIMIT, according to the bug I mentioned.
staticsan
+1. Hadn't heard about the bug before
Manos Dilaverakis
+2  A: 

In most situations it is much faster and less resource intensive to do it in two separate queries than to do it in one, even though that seems counter-intuitive.

If you use SQL_CALC_FOUND_ROWS, then for large tables it makes your query much slower, significantly slower even than executing two queries, the first with a COUNT(*) and the second with a LIMIT. The reason for this is that SQL_CALC_FOUND_ROWS causes the LIMIT clause to be applied after fetching the rows instead of before, so it fetches the entire row for all possible results before applying the limits. This can't be satisfied by an index because it actually fetches the data.

If you take the two queries approach, the first one only fetching COUNT(*) and not actually fetching and actual data, this can be satisfied much more quickly because it can usually use indexes and doesn't have to fetch the actual row data for every row it looks at. Then, the second query only needs to look at the first $offset+$limit rows and then return.

This post from the MySQL performance blog explains this further.

For more information on optimising pagination, check this post and this post.

thomasrutter
+1  A: 

I never do two queries.

Simply return one more row than is needed, only display 10 on the page, and if there are more than are displayed, display a "Next" button.

SELECT COUNT(*) FROM table WHERE some_condition LIMIT 0, 11

// iterate through and display 10 rows.

// if there were 11 rows, display a "Next" button.

Your query should return in an order of most relevant first. Chances are, most people aren't going to care about going to page 236 out of 412.

When you do a google search, and your results aren't on the first page, you likely go to page two, not nine.

Derrick
This is true, I will keep that in mind.
Jasie