MySQL: Pagination - SQL_CALC_FOUND_ROWS vs COUNT()-Query

Permanent Link: MySQL: Pagination - SQL_CALC_FOUND_ROWS vs COUNT()-Query 17. Juni 2009 RSS Feed for comments on RSS-Feed für Kommentare zu: MySQL: Pagination - SQL_CALC_FOUND_ROWS vs COUNT()-Query comments feed

When using pagination for let's say a list of offers that where never clicked, you have to know the exact amount of offers (which were never clicked) in order to know how many pages you have. Now there are 2 possible ways of calculating the exact amount of offers: You can use either SQL_CALC_FOUND_ROWS or you can setup a second query with a COUNT() in it. I did the tests with SQL_NO_CACHE in order to get the best results possible. The clicks table has about 18.000.000 rows, the offer table about 800.000. Let's start with some time results.

Using SQL_CALC_FOUND_ROWS:

mysql> SELECT SQL_NO_CACHE SQL_CALC_FOUND_ROWS o.offer_id
-> FROM offer AS o
-> LEFT JOIN clicks AS c
-> ON (o.offer_id = c.offer_id)
-> WHERE c.offer_id IS NULL
-> LIMIT 50,50;
50 rows in set (30.84 sec)

mysql> SELECT FOUND_ROWS();
1 row in set (0.00 sec)

Using a second query with COUNT():

mysql> SELECT SQL_NO_CACHE o.offer_id
-> FROM offer AS o
-> LEFT JOIN clicks AS c
-> ON (o.offer_id = c.offer_id)
-> WHERE c.offer_id IS NULL
-> LIMIT 50,50;
50 rows in set (0.03 sec)

mysql> SELECT SQL_NO_CACHE COUNT(o.offer_id)
-> FROM offer AS o
-> LEFT JOIN clicks AS c
-> ON (o.offer_id = c.offer_id)
-> WHERE c.offer_id IS NULL;
1 row in set (30.97 sec)

At a first glance they look equally fast, both taking about 30 seconds. But: They are only equally fast, when query caching is turned off. Let's assume we're on a high-traffic website where performance matters, so we turn the query cache on. MySQL Query caching is like a key-value cache with the key being the EXACT query and the resultset being the value. Once we turn on the cache, the pagination is way faster with the second query using COUNT().

Why?

When using SQL_CALC_FOUND_ROWS the application has to calculate the found rows every single time we turn the page, because the query changes, while the COUNT()-Query always remains the same, meaning that its result comes from the query cache from the second time on. Let's emulate:

Using SQL_CALC_FOUND_ROWS:

mysql> SELECT SQL_CALC_FOUND_ROWS o.offer_id
-> FROM offer AS o
-> LEFT JOIN clicks AS c
-> ON (o.offer_id = c.offer_id)
-> WHERE c.offer_id IS NULL
-> LIMIT 50,50;
50 rows in set (31.13 sec)

mysql> SELECT FOUND_ROWS();
1 row in set (0.00 sec)

mysql> SELECT SQL_CALC_FOUND_ROWS o.offer_id
-> FROM offer AS o
-> LEFT JOIN clicks AS c ON (o.offer_id = c.offer_id)
-> WHERE c.offer_id IS NULL
-> LIMIT 100,50;
50 rows in set (30.71 sec)

mysql> SELECT FOUND_ROWS();
1 row in set (0.00 sec)

Using a second query with COUNT():

mysql> SELECT o.offer_id
-> FROM offer AS o
-> LEFT JOIN clicks AS c
-> ON (o.offer_id = c.offer_id)
-> WHERE c.offer_id IS NULL
-> LIMIT 50,50;
50 rows in set (0.03 sec)

mysql> SELECT COUNT(o.offer_id)
-> FROM offer AS o
-> LEFT JOIN clicks AS c
-> ON (o.offer_id = c.offer_id)
-> WHERE c.offer_id IS NULL;
1 row in set (31.11 sec)

mysql> SELECT o.offer_id
-> FROM offer AS o
-> LEFT JOIN clicks AS c
-> ON (o.offer_id = c.offer_id)
-> WHERE c.offer_id IS NULL
-> LIMIT 100,50;
50 rows in set (0.04 sec)

mysql> SELECT COUNT(o.offer_id)
-> FROM offer AS o
-> LEFT JOIN clicks AS c
-> ON (o.offer_id = c.offer_id)
-> WHERE c.offer_id IS NULL;
1 row in set (0.00 sec)

Et Voilà! While the SQL_CALC_FOUND_ROWS Queries took more than one minute together, the queries with the second COUNT() query only took a bit more than 30 seconds together, meaning they are twice as fast.

I'm pretty sure there are situations where SQL_CALC_FOUND_ROWS is the way to go, but in cases like this one you definately wanna go for the COUNT()-Query.

13 comments

Srinis Gravatar

Srini
30.06.2009, 19:10 o'clock

Cool! Its 100% True!

Ronald H.s Gravatar

Ronald H.
02.07.2009, 03:30 o'clock

Thanks buddy. I'll be using this code in the future. I also thought you'd like to know there is a great domain name at Godaddy.com that you may be interested in. It's call PHPDEVELOPING.COM and I think its a good fit for you because your a great PHP programmer. You can contact me at my email address and I'll help you get to it if you want. Again, just thought you'd like to know.

Dominik Jungowskis Gravatar

Dominik Jungowski
02.07.2009, 11:21 o'clock

Just keep in mind that it's not always THE solution. When reading from a table that is very frequently updated (more than once a minute), you won't get too much speed benefits, as with each table update the cache query for the respective table runs out.

Thanks for the address tip, I'll think about it

wasimasifs Gravatar

wasimasif
15.07.2009, 15:48 o'clock

In my experience sometimes its good to use SQL_CALC_FOUND_ROWS instead of running separate queries for count.

If you are running full-text query on table with 2M+ rows then its good idea to use it. When we tried the query with SQL_CALC_FOUND_ROWS, performed better than two separate queries.

I also agree to prefer SQL_CALC_FOUND_ROWS on frequently updated table.

phps Gravatar

php
21.07.2009, 16:56 o'clock

thanks for great post

pe3soss Gravatar

pe3sos
12.10.2009, 15:42 o'clock

Cool very beatiful, thanks a lot!!!!

shanmuganathans Gravatar

shanmuganathan
27.11.2010, 13:02 o'clock

We are one of the Leading Web Design Company in chennai,our services are web design,web hosting,seo chennai,email marketing,business blogging,mobile application developing ,brochures designers,logo designers ,graphic designers,flash designers in chennai and san diego.please add my website to your blogger.it is very useful for us.

Hot Water Systemss Gravatar

Hot Water Systems
04.04.2011, 07:18 o'clock

Thank you, this site has given us an useful information

Siegfrieds Gravatar

Siegfried
18.05.2011, 02:06 o'clock

SQL_CALC_FOUND_ROWS is slower when the field for its arguments are using index. I that true?

Dominik Jungowskis Gravatar

Dominik Jungowski
22.05.2011, 16:07 o'clock

I don't know, but this statement seems to be a bit odd to me. A benchmark would be helpful for that. Where do you have it from?

grid inverterss Gravatar

grid inverters
26.05.2011, 06:50 o'clock

Cool very beatiful, thanks a lot!!!!

Siegfrieds Gravatar

Siegfried
29.05.2011, 00:44 o'clock

Yes it's true dominic. I had a test. SQL_CALC_FOUND_ROWS is faster without index. If you have indexed field, better adding IGNORE INDEX.

SELECT SQL_CALC_FOUND_ROWS * FROM table IGNORE INDEX(id) WHERE id = 1 LIMIT n

CMIIW

Dominik Jungowskis Gravatar

Dominik Jungowski
02.06.2011, 18:36 o'clock

Interesting, I have to try that one out. Hope I will think about it tomorrow

Write a comment

(will not be published)