<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
		>
<channel>
	<title>Comments on: Four ways to optimize paginated displays</title>
	<atom:link href="http://www.mysqlperformanceblog.com/2008/09/24/four-ways-to-optimize-paginated-displays/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.mysqlperformanceblog.com/2008/09/24/four-ways-to-optimize-paginated-displays/</link>
	<description>Percona&#039;s MySQL &#38; InnoDB performance and scalability blog</description>
	<lastBuildDate>Sat, 11 Feb 2012 16:45:54 +0000</lastBuildDate>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
	<item>
		<title>By: Robert Eisele</title>
		<link>http://www.mysqlperformanceblog.com/2008/09/24/four-ways-to-optimize-paginated-displays/comment-page-1/#comment-783337</link>
		<dc:creator>Robert Eisele</dc:creator>
		<pubDate>Thu, 18 Nov 2010 16:54:53 +0000</pubDate>
		<guid isPermaLink="false">http://www.mysqlperformanceblog.com/?p=508#comment-783337</guid>
		<description>I forgot to mention, that using MyISAM for _tmp is great to perform statistical queries also very fast. To be able to generate the links without estimation use:

SELECT COUNT(*) FROM _tmp;</description>
		<content:encoded><![CDATA[<p>I forgot to mention, that using MyISAM for _tmp is great to perform statistical queries also very fast. To be able to generate the links without estimation use:</p>
<p>SELECT COUNT(*) FROM _tmp;</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Robert Eisele</title>
		<link>http://www.mysqlperformanceblog.com/2008/09/24/four-ways-to-optimize-paginated-displays/comment-page-1/#comment-783334</link>
		<dc:creator>Robert Eisele</dc:creator>
		<pubDate>Thu, 18 Nov 2010 16:47:28 +0000</pubDate>
		<guid isPermaLink="false">http://www.mysqlperformanceblog.com/?p=508#comment-783334</guid>
		<description>Using the estimation of EXPLAIN is a really cool idea. For most of my paginations, I created a different approach (and even use different approaches in one application, but this one is also added to my recently published SQL class), which lacks on a single O(n) but the rest is really fast. So if you only copy the id&#039;s this way, you can perform the query on a result set with lets say 10 millions rows under 1s (on the right hardware). Okay you should cache the result, because it is not fair on concurrency, but here the queries I run when I want to sort for the just calculated column &quot;rank&quot; and narrow down the result with $offset and $limit:

# create a temporary table with all elements and a key on rank
CREATE TEMPORARY TABLE _tmp (KEY SORT(rank DESC))
SELECT id, rank FROM table;

# add a primary key for a fast lookup
ALTER TABLE _tmp ADD OFFSET INT UNSIGNED PRIMARY KEY AUTO_INCREMENT, DROP INDEX SORT, ORDER BY rank;

# scan the primary key and get the result, maybe add a join to get real data
SELECT id FROM _tmp WHERE OFFSET &gt;= $offset ORDER BY OFFSET LIMIT $limit;

Robert</description>
		<content:encoded><![CDATA[<p>Using the estimation of EXPLAIN is a really cool idea. For most of my paginations, I created a different approach (and even use different approaches in one application, but this one is also added to my recently published SQL class), which lacks on a single O(n) but the rest is really fast. So if you only copy the id&#8217;s this way, you can perform the query on a result set with lets say 10 millions rows under 1s (on the right hardware). Okay you should cache the result, because it is not fair on concurrency, but here the queries I run when I want to sort for the just calculated column &#8220;rank&#8221; and narrow down the result with $offset and $limit:</p>
<p># create a temporary table with all elements and a key on rank<br />
CREATE TEMPORARY TABLE _tmp (KEY SORT(rank DESC))<br />
SELECT id, rank FROM table;</p>
<p># add a primary key for a fast lookup<br />
ALTER TABLE _tmp ADD OFFSET INT UNSIGNED PRIMARY KEY AUTO_INCREMENT, DROP INDEX SORT, ORDER BY rank;</p>
<p># scan the primary key and get the result, maybe add a join to get real data<br />
SELECT id FROM _tmp WHERE OFFSET &gt;= $offset ORDER BY OFFSET LIMIT $limit;</p>
<p>Robert</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: trr</title>
		<link>http://www.mysqlperformanceblog.com/2008/09/24/four-ways-to-optimize-paginated-displays/comment-page-1/#comment-490270</link>
		<dc:creator>trr</dc:creator>
		<pubDate>Thu, 26 Feb 2009 13:12:52 +0000</pubDate>
		<guid isPermaLink="false">http://www.mysqlperformanceblog.com/?p=508#comment-490270</guid>
		<description>@Sam,

Could that difference have been caused by a large part of that query being cached the second time?</description>
		<content:encoded><![CDATA[<p>@Sam,</p>
<p>Could that difference have been caused by a large part of that query being cached the second time?</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Bill</title>
		<link>http://www.mysqlperformanceblog.com/2008/09/24/four-ways-to-optimize-paginated-displays/comment-page-1/#comment-375935</link>
		<dc:creator>Bill</dc:creator>
		<pubDate>Tue, 11 Nov 2008 21:52:59 +0000</pubDate>
		<guid isPermaLink="false">http://www.mysqlperformanceblog.com/?p=508#comment-375935</guid>
		<description>I am with Baron on my app. There is no way to maintain count tables if they possibly searching on any combination of 30 different fields. I also tried Chad Walkers method without success. I have many joins when using count() got 107 for total rows.  When multiplying got 423654. Not sure if my query has other optimization problems but that estimate is far from usable.</description>
		<content:encoded><![CDATA[<p>I am with Baron on my app. There is no way to maintain count tables if they possibly searching on any combination of 30 different fields. I also tried Chad Walkers method without success. I have many joins when using count() got 107 for total rows.  When multiplying got 423654. Not sure if my query has other optimization problems but that estimate is far from usable.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Sam</title>
		<link>http://www.mysqlperformanceblog.com/2008/09/24/four-ways-to-optimize-paginated-displays/comment-page-1/#comment-362752</link>
		<dc:creator>Sam</dc:creator>
		<pubDate>Fri, 17 Oct 2008 18:09:32 +0000</pubDate>
		<guid isPermaLink="false">http://www.mysqlperformanceblog.com/?p=508#comment-362752</guid>
		<description>Baron,

I had a slow query that involved pagination, so I investigated after reading your post on the matter. The result of my investigation was very interesting. Basically, I had a query that was taking about 3 seconds to execute. The SELECT portion of the query was specifying which columns to fetch (i.e. SELECT table.col2, table.col2, ... table.col9). When I simply changed this to &quot;SELECT table.*&quot; the query time dropped to 0.01 sec.! So, in the end, it did not appear that the slowness was a result of the pagination at all (the query uses ORDER BY, GROUP BY, WHERE and LIMIT and orders on a non-indexed field.) Using EXPLAIN did not show any different in the query optimization path for the two forms of query either.  MySQL bug?

Sam</description>
		<content:encoded><![CDATA[<p>Baron,</p>
<p>I had a slow query that involved pagination, so I investigated after reading your post on the matter. The result of my investigation was very interesting. Basically, I had a query that was taking about 3 seconds to execute. The SELECT portion of the query was specifying which columns to fetch (i.e. SELECT table.col2, table.col2, &#8230; table.col9). When I simply changed this to &#8220;SELECT table.*&#8221; the query time dropped to 0.01 sec.! So, in the end, it did not appear that the slowness was a result of the pagination at all (the query uses ORDER BY, GROUP BY, WHERE and LIMIT and orders on a non-indexed field.) Using EXPLAIN did not show any different in the query optimization path for the two forms of query either.  MySQL bug?</p>
<p>Sam</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Baron Schwartz</title>
		<link>http://www.mysqlperformanceblog.com/2008/09/24/four-ways-to-optimize-paginated-displays/comment-page-1/#comment-361392</link>
		<dc:creator>Baron Schwartz</dc:creator>
		<pubDate>Sun, 12 Oct 2008 17:51:30 +0000</pubDate>
		<guid isPermaLink="false">http://www.mysqlperformanceblog.com/?p=508#comment-361392</guid>
		<description>rar, for SOME applications.  It is absolutely not The End for all.  For many apps I work on, that method would a) not work because there are too many permutations of criteria to count and b) if you could do it, it&#039;d load the site much more heavily than not having counter tables.</description>
		<content:encoded><![CDATA[<p>rar, for SOME applications.  It is absolutely not The End for all.  For many apps I work on, that method would a) not work because there are too many permutations of criteria to count and b) if you could do it, it&#8217;d load the site much more heavily than not having counter tables.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: rar</title>
		<link>http://www.mysqlperformanceblog.com/2008/09/24/four-ways-to-optimize-paginated-displays/comment-page-1/#comment-360989</link>
		<dc:creator>rar</dc:creator>
		<pubDate>Fri, 10 Oct 2008 00:48:45 +0000</pubDate>
		<guid isPermaLink="false">http://www.mysqlperformanceblog.com/?p=508#comment-360989</guid>
		<description>Ditto David. Maintain count tables. The End.</description>
		<content:encoded><![CDATA[<p>Ditto David. Maintain count tables. The End.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Baron Schwartz</title>
		<link>http://www.mysqlperformanceblog.com/2008/09/24/four-ways-to-optimize-paginated-displays/comment-page-1/#comment-359383</link>
		<dc:creator>Baron Schwartz</dc:creator>
		<pubDate>Fri, 03 Oct 2008 13:26:30 +0000</pubDate>
		<guid isPermaLink="false">http://www.mysqlperformanceblog.com/?p=508#comment-359383</guid>
		<description>David, thanks for sharing your approach.  That looks like a great way to do it.  I think it would be hard to apply to search results (as on a shopping site) where searching is the norm and showing by category is less used, but in this case it looks like a perfect match.  Knowing your data and access patterns lets you do all kinds of good things!</description>
		<content:encoded><![CDATA[<p>David, thanks for sharing your approach.  That looks like a great way to do it.  I think it would be hard to apply to search results (as on a shopping site) where searching is the norm and showing by category is less used, but in this case it looks like a perfect match.  Knowing your data and access patterns lets you do all kinds of good things!</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: David BL</title>
		<link>http://www.mysqlperformanceblog.com/2008/09/24/four-ways-to-optimize-paginated-displays/comment-page-1/#comment-359345</link>
		<dc:creator>David BL</dc:creator>
		<pubDate>Fri, 03 Oct 2008 09:10:25 +0000</pubDate>
		<guid isPermaLink="false">http://www.mysqlperformanceblog.com/?p=508#comment-359345</guid>
		<description>I have recently added paging to my website. First attempt used SQL_COUNT_FOUND_ROWS everywhere. This, unfortunately, slowed things down considerably.

My solution that works very well and has good performance and does NOT remove functionality as this article suggests is:

I&#039;ll explain how I do it for the forum:
-----------------------------------------------
1) I have a statistics table that is updated by triggers on inserts:
 statistics_table(serviceid, count, lastinsert):
 +--------+---------+----------------+
 &#124; SERVICE&#124; COUNTER &#124; LASTINSERT     &#124;
 &#124;--------&#124;---------&#124;----------------&#124;
 &#124; forum  &#124; 10020202&#124; 2008-10-03.... &#124;
 &#124; users  &#124;    10002&#124; 2008-09-13.... &#124;

(I use InnoDB so getting count(*) is too slow)

2) Per forum category I added the same statistics per item in the forum-category table. Again, this table is updated with the same trigger that updates the statistics_table

 +---------+---------+----------------+
 &#124; CATEGORY&#124; COUNTER &#124; LASTINSERT     &#124;
 &#124;---------&#124;---------&#124;----------------&#124;
 &#124; GENERAL &#124;    10001&#124; 2008-10-03.... &#124;
 &#124; TOPIC1  &#124;     1002&#124; 2008-09-13.... &#124;
 &#124; TOPIC2  &#124;     4002&#124; 2008-09-13.... &#124;


Then, based on the query&#039;s where-statements I get the total rows available if its an empty where statement or the counter from the forum-category table if filtered on a category. If its anything else, like free text search or filtered on a user etc. then I fall back to the SQL_COUNT_FOUND_ROWS method.
The tables are so small that they are always cached. I wanted to use in-memory engine, but for some reason I got empty results when querying the forum-category table when in memory.

Making paging links is then easy using (total rows)/(rows per page)

David</description>
		<content:encoded><![CDATA[<p>I have recently added paging to my website. First attempt used SQL_COUNT_FOUND_ROWS everywhere. This, unfortunately, slowed things down considerably.</p>
<p>My solution that works very well and has good performance and does NOT remove functionality as this article suggests is:</p>
<p>I&#8217;ll explain how I do it for the forum:<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
1) I have a statistics table that is updated by triggers on inserts:<br />
 statistics_table(serviceid, count, lastinsert):<br />
 +&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
 | SERVICE| COUNTER | LASTINSERT     |<br />
 |&#8212;&#8212;&#8211;|&#8212;&#8212;&#8212;|&#8212;&#8212;&#8212;&#8212;&#8212;-|<br />
 | forum  | 10020202| 2008-10-03&#8230;. |<br />
 | users  |    10002| 2008-09-13&#8230;. |</p>
<p>(I use InnoDB so getting count(*) is too slow)</p>
<p>2) Per forum category I added the same statistics per item in the forum-category table. Again, this table is updated with the same trigger that updates the statistics_table</p>
<p> +&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
 | CATEGORY| COUNTER | LASTINSERT     |<br />
 |&#8212;&#8212;&#8212;|&#8212;&#8212;&#8212;|&#8212;&#8212;&#8212;&#8212;&#8212;-|<br />
 | GENERAL |    10001| 2008-10-03&#8230;. |<br />
 | TOPIC1  |     1002| 2008-09-13&#8230;. |<br />
 | TOPIC2  |     4002| 2008-09-13&#8230;. |</p>
<p>Then, based on the query&#8217;s where-statements I get the total rows available if its an empty where statement or the counter from the forum-category table if filtered on a category. If its anything else, like free text search or filtered on a user etc. then I fall back to the SQL_COUNT_FOUND_ROWS method.<br />
The tables are so small that they are always cached. I wanted to use in-memory engine, but for some reason I got empty results when querying the forum-category table when in memory.</p>
<p>Making paging links is then easy using (total rows)/(rows per page)</p>
<p>David</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: chad walker</title>
		<link>http://www.mysqlperformanceblog.com/2008/09/24/four-ways-to-optimize-paginated-displays/comment-page-1/#comment-359067</link>
		<dc:creator>chad walker</dc:creator>
		<pubDate>Wed, 01 Oct 2008 14:22:57 +0000</pubDate>
		<guid isPermaLink="false">http://www.mysqlperformanceblog.com/?p=508#comment-359067</guid>
		<description>Oh well, it used to formatted nicely... The point is, you can run explain&#039;s just like any other query. To get an estimate on the number of rows, multiple all of the &#039;rows&#039; columns together (if there is more then one table involved).</description>
		<content:encoded><![CDATA[<p>Oh well, it used to formatted nicely&#8230; The point is, you can run explain&#8217;s just like any other query. To get an estimate on the number of rows, multiple all of the &#8216;rows&#8217; columns together (if there is more then one table involved).</p>
]]></content:encoded>
	</item>
</channel>
</rss>

