# 优化 SQL\_CALC\_FOUND\_ROWS

分页的时候，一个常用的技巧是在 LIMIT 语句中加上 <mark style="color:blue;">**SQL\_CALC\_FOUND\_ROWS**</mark> 提示(hint)，这样就可以获得去掉 LIMIT 子句后满足条件的行数，因此可以作为<mark style="color:blue;">**分页的总数**</mark>。

MySQL 只有在扫描了所有满足条件的行以后，才会知道行数，所以<mark style="color:orange;">**加上这个提示以后，不管是否需要， MySQL 都会扫描所有满足条件的行，然后再抛弃掉不需要的行**</mark>，而不是在满足 LIMIT 的行数后就终止扫描。所以<mark style="color:red;">**该提示的代价可能非常高**</mark>。

1. <mark style="color:orange;">**一个更好的设计是将具体的页数换成“下一页”按钮**</mark>，假设每页显示 20 条记录，那么我们每次查询时都是用 LIMIT 返回 21 条记录并只显示 20 条，如果第 21 条存在，那么就显示“下一页”按钮，否则就说明没有更多的数据，也就无须显示“下一页”按钮了。
2. <mark style="color:orange;">**另一种做法是先获取并缓存较多的数据——例如，缓存 1000 条——然后每次分页都从这个缓存中获取**</mark>。这样做可以让应用程序根据结果集的大小采取不同的策略，如果结果集小于 1000，就可以在页面上显示所有的分页链接，因为数据都在缓存中，所以这样做不会对性能造成影响。<mark style="color:orange;">**如果结果集大于 1000，则可以在页面上设计一个额外的“找到的结果多于 1000 条”之类的按钮。**</mark>这两种策略都比每次生成全部结果集再抛弃不需要的数据的效率高很多。
3. 有时候也<mark style="color:orange;">**可以考虑使用 EXPLAIN 的结果中的 rows 列的值来作为结果集总数的近似值，**</mark>当需要精确结果的时候，再单独使用 COUNT(\*) 来满足需求。

> <pre class="language-sql"><code class="lang-sql">mysql> SELECT SQL_CALC_FOUND_ROWS *
>        FROM rental
> <strong>       LIMIT 5, 10;
> </strong><strong>mysql> SELECT FOUND_ROWS();
> </strong><strong>
> </strong>+--------------+
> | FOUND_ROWS() |
> +--------------+
> |        16044 |
> +--------------+
> </code></pre>
