144

Which way to count a number of rows should be faster in MySQL?

This:

SELECT COUNT(*) FROM ... WHERE ...

Or, the alternative:

SELECT 1 FROM ... WHERE ...

// and then count the results with a built-in function, e.g. in PHP mysql_num_rows()

One would think that the first method should be faster, as this is clearly database territory and the database engine should be faster than anybody else when determining things like this internally.

Franz
  • 11,353
  • 8
  • 48
  • 70
  • 2
    Oh, I found a similar question (http://stackoverflow.com/questions/1855226/mysql-count-rows-performance). But then, I use `SELECT 1` and not `SELECT *`. Is there a difference? – Franz Feb 20 '11 at 22:00
  • i don't know, but it is conceivable that these two answers are identical -- the mysql query optimizer may do the same thing on each. that said the former is less ambiguous than the latter. why don't you write some benchmarks and test it out? – Jesse Cohen Feb 20 '11 at 22:06
  • Uhm, let's assume I'm trying to enhance SO's search engine visibility by asking a similar question in different words ;) – Franz Feb 20 '11 at 22:37
  • 1
    The difference is the amount of data sent over to the PHP side. The more columns you have, the slower SELECT * gets relative to SELECT 1, because all columns are retrieved instead of just the number 1. When you run `mysql_query()`, for instance, the entire result set is sent to PHP from MySQL, regardless of what you do with that data. – toon81 Feb 26 '13 at 08:28
  • Asking a question like this is a great way to gain insight or new ideas, but ultimately if you actually have a specific scenario where you want more speed, you will have to run tests to see what is the fastest. – still_dreaming_1 Dec 17 '15 at 20:29
  • @Franz - `SELECT 1 ...` delivers a bunch of `1`. `SELECT *` gathers all the columns for all the rows. The latter is, in some situations, order of magnitude slower if all you want is a count. – Rick James Jun 08 '18 at 02:00

14 Answers14

144

When you COUNT(*) it takes in count column indexes, so it will be the best result. MySQL with MyISAM engine actually stores row count, it doesn't count all rows each time you try to count all rows. (based on primary key's column)

Using PHP to count rows is not very smart, because you have to send data from MySQL to PHP. Why do it when you can achieve the same on the MySQL side?

If the COUNT(*) is slow, you should run EXPLAIN on the query, and check if indexes are really used, and where they should be added.


The following is not the fastest way, but there is a case, where COUNT(*) doesn't really fit - when you start grouping results, you can run into a problem where COUNT doesn't really count all rows.

The solution is SQL_CALC_FOUND_ROWS. This is usually used when you are selecting rows but still need to know the total row count (for example, for paging). When you select data rows, just append the SQL_CALC_FOUND_ROWS keyword after SELECT:

SELECT SQL_CALC_FOUND_ROWS [needed fields or *] FROM table LIMIT 20 OFFSET 0;

After you have selected needed rows, you can get the count with this single query:

SELECT FOUND_ROWS();

FOUND_ROWS() has to be called immediately after the data selecting query.


In conclusion, everything actually comes down to how many entries you have and what is in the WHERE statement. You should really pay attention on how indexes are being used, when there are lots of rows (tens of thousands, millions, and up).

Josh Correia
  • 3,807
  • 3
  • 33
  • 50
Mārtiņš Briedis
  • 17,396
  • 5
  • 54
  • 76
  • 15
    Correction: `MyISAM` stores row count. Other storage engines like `InnoDB` **do not** store row counts and *will count all rows each time*. – The Scrum Meister Feb 21 '11 at 00:06
  • 1
    Do you know which will be fastest when you simply want to find out whether there is a row: `SELECT 1 FROM ... LIMIT 1` or `SELECT COUNT(*) FROM ...`? – Franz Mar 18 '11 at 20:29
  • 1
    It's probably useful to note that if you need the data anyway and only want a count for pagination/etc. it is more efficient to get the data then count the rows in your program. – Tyzoid Aug 01 '13 at 20:16
  • @Tyzoid unless you're fetching directly to an associative array where the array stores the amount of elements in the array, your suggestion is false. Generally speaking, is always faster to count on the database, and then just pull that number from your application's language. – ILikeTacos Nov 13 '13 at 18:57
  • 7
    It's irrelevant whether the engine stores row counts. The question clearly states there's a `WHERE` clause. – Álvaro González Jan 23 '14 at 12:43
  • 1
    @Franz `SELECT COUNT(*) FROM ...` can take considerable time, depending on what has to be scanned (e.g. a very large table or index of millions/billions/trillions of rows). `SELECT 1 FROM ... LIMIT 1` returns immediately because you're limiting it to the first row. – jbo5112 Apr 15 '14 at 17:15
  • 1
    Using `explain` in `select` with `limit` should be enough to show the row count of a table. We can see the result of `explain select * from tablename limit 1;`, there will be a column that shows the number of rows of the table. It takes no time in my machine to execute it. (my table has 14million rows and 2GB as it's size) – Oki Erie Rinaldi Aug 12 '16 at 03:33
  • @OkiErieRinaldi tha'ts fine and dandy until there are where statements. There will be no difference between calling count(*), because table(index) still needs to be scanned to get the count. – Mārtiņš Briedis Aug 12 '16 at 06:58
  • And if you are looking for "why the 'row' result of `explain` is not accurate", here is the answer http://stackoverflow.com/questions/1037471/why-the-rows-returns-by-explain-is-not-equal-to-count – Oki Erie Rinaldi Aug 18 '16 at 08:26
  • @TheScrumMeister - InnoDB counts all the rows, yes. But it tries to take a slight shortcut of using the smallest index. – Rick James Jun 08 '18 at 02:02
  • 1
    @TheScrumMeister - MyISAM stores _only_ the count for the entire table; so this applies _only_ when you don't have a `WHERE`. – Rick James Jun 08 '18 at 02:04
  • `EXPLAIN` gives an _estimate_ of the number rows. Yes it is faster, but it is less accurate. – Rick James Jun 08 '18 at 02:05
  • @Franz - The fastest for "existence": `EXISTS ( SELECT 1 FROM ... WHERE ... )` – Rick James Jun 02 '19 at 21:15
  • @Tyzoid - No. When paginating, one gets only some of the rows. `SQL_CALC_FOUND_ROWS` does not fetch the rest of the rows, it simply counts them. This may, for example, involve scanning only the index, not the data. – Rick James Jun 02 '19 at 21:17
  • 1
    @jbo5112 - Not if there is a `WHERE` clause and the table is poorly indexed; it may still have to scan the entire table to decide whether any row exists that matches the `WHERE`. – Rick James Jun 02 '19 at 21:18
  • @RickJames Wow, this takes me back. In general - yes, for pagination, you'll want to select a small number of results at one time. The intent of comment was specifically that if you need *all* the data, there's no need to go to the database multiple times, either to do a `LIMIT n OFFSET m` or `COUNT(*)`. – Tyzoid Jun 02 '19 at 21:22
  • 1
    FOUND_ROWS and SQL_CALC_FOUND_ROWS are depreciated and won't be used in versions beyond MySQL 8.0.17. http://dev.mysql.com/doc/refman/5.0/en/information-functions.html#function_found-rows – napierjohn Feb 25 '23 at 14:58
90

After speaking with my team-mates, Ricardo told us that the faster way is:

show table status like '<TABLE NAME>' \G

But you have to remember that the result may not be exact.

You can use it from command line too:

$ mysqlshow --status <DATABASE> <TABLE NAME>

More information: http://dev.mysql.com/doc/refman/5.7/en/show-table-status.html

And you can find a complete discussion at mysqlperformanceblog

MagMax
  • 1,645
  • 2
  • 17
  • 26
  • 5
    For InnoDB, this is an approximation. – Martin Tournoij Aug 27 '14 at 13:24
  • 2
    This is great to know when needing rough idea of the number of rows in very large tables where count(*) can literally take hours! – Mark Hansen Mar 15 '15 at 22:42
  • This saved me from pulling all my hairs out. COUNT(*) was taking ages to count all 33 million plus rows in my database. Anyway, I only wanted to know if my parallelized delete rows function was working or not. I didn't need an exact number. – joemar.ct Jun 28 '15 at 03:01
  • 1
    +1 Using the table status instead "COUNT(*)" should be the correct answer to this question as is about "fastest" not "accuracy". – lepe Oct 20 '15 at 01:05
  • 5
    Using `SHOW TABLE STATUS` (or the equivalent `SELECT` in `information_schema`) is fast, but it does not handle a `WHERE` clause. It is precise for MyISAM, but imprecise (sometimes off by a factor of 2) for InnoDB. – Rick James Dec 28 '15 at 22:08
31

This query (which is similar to what bayuah posted) shows a nice summary of all tables count inside a database: (simplified version of stored procedure by Ivan Cachicatari which I highly recommend).

SELECT TABLE_NAME AS 'Table Name', TABLE_ROWS AS 'Rows' FROM information_schema.TABLES WHERE TABLES.TABLE_SCHEMA = '`YOURDBNAME`' AND TABLES.TABLE_TYPE = 'BASE TABLE'; 

Example:

+-----------------+---------+
| Table Name      | Rows    |
+-----------------+---------+
| some_table      |   10278 |
| other_table     |     995 |
Giacomo1968
  • 25,759
  • 11
  • 71
  • 103
lepe
  • 24,677
  • 9
  • 99
  • 108
  • It gives me a result. But the results from count(1) and this one are different. This way gives a less number always than count query. Any thoughts? – Ayyappan Sekar Oct 10 '18 at 04:58
  • 4
    Just a note to the readers. This method is extremely fast but it is only applicable when you can work with approximated number of rows since the value stored in `information_schema` is not the same as the one returned by `SELECT count(*) FROM` in case InnoDB is used. If you need strict value then keep in mind that this method gives strict value only with MyISAM tables. With InnoDB the number of rows is rough approximation. – Bartosz Firyn Aug 27 '19 at 13:28
28

Great question, great answers. Here's a quick way to echo the results if anyone is reading this page and missing that part:

$counter = mysql_query("SELECT COUNT(*) AS id FROM table");
$num = mysql_fetch_array($counter);
$count = $num["id"];
echo("$count");
BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
Dan Horvat
  • 800
  • 3
  • 14
  • 27
12

I've always understood that the below will give me the fastest response times.

SELECT COUNT(1) FROM ... WHERE ...
adarshr
  • 61,315
  • 23
  • 138
  • 167
8

Try this:

SELECT
    table_rows "Rows Count"
FROM
    information_schema.tables
WHERE
    table_name="Table_Name"
AND
    table_schema="Database_Name";
bayuah
  • 251
  • 1
  • 5
  • 10
  • 1
    @lepe I'm sorry. I meant, it's really nice if someone who did downvoting give some explanation why he/she do that, so everybody can learn something about it. – bayuah Oct 04 '16 at 09:08
  • 1
    This will give you an *approximate* answer quickly. If you need an exact answer, you need to perform `select count(*) from table_name` or something else. http://dba.stackexchange.com/questions/151769/mysql-difference-between-using-count-and-information-schema-tables-for-coun?noredirect=1#comment289141_151769 – Programster Oct 08 '16 at 08:26
  • @Programster Thank you. It's better than leave me in the dark for almost a year. – bayuah Nov 28 '16 at 22:32
  • 1
    @bayuah I'm not sure what you meant by your last comment. I can only assume you think I am the one who down-voted your answer, which I'm not. – Programster Nov 29 '16 at 10:34
  • 1
    @Programster No, I'm sorry, I didn't mean that. I meant thank you for your explanation, so I can conjecture what maybe Downvoter thought when he/she do that. – bayuah Nov 29 '16 at 11:17
7

If you need to get the count of the entire result set you can take following approach:

SELECT SQL_CALC_FOUND_ROWS * FROM table_name LIMIT 5;
SELECT FOUND_ROWS();

This isn't normally faster than using COUNT albeit one might think the opposite is the case because it's doing the calculation internally and doesn't send the data back to the user thus the performance improvement is suspected.

Doing these two queries is good for pagination for getting totals but not particularly for using WHERE clauses.

Alex Rashkov
  • 9,833
  • 3
  • 32
  • 58
  • Intersting. Does that work across the most common database systems? MySQL, Postgres, SQLite...? – Franz Nov 12 '12 at 22:24
  • 4
    This is actually often not faster than using COUNT(*) at all. See http://stackoverflow.com/questions/186588/which-is-fastest-select-sql-calc-found-rows-from-table-or-select-count – toon81 Feb 26 '13 at 08:25
  • 3
    You should be VERY careful when using this function. Its reckless use once brought our entire production environment to a grinding halt. It is VERY resource intensive, so use with care. – Janis Peisenieks Dec 13 '13 at 09:06
7

I did some benchmarks to compare the execution time of COUNT(*) vs COUNT(id) (id is the primary key of the table - indexed).

Number of trials: 10 * 1000 queries

Results: COUNT(*) is faster 7%

VIEW GRAPH: benchmarkgraph

My advice is to use: SELECT COUNT(*) FROM table

SamuelCarreira
  • 101
  • 1
  • 4
  • 5
    FYI there is also a common way to count with `COUNT(1)`, would be interesting to see some benchmarks there... – Sliq Jan 28 '20 at 16:00
6

EXPLAIN SELECT id FROM .... did the trick for me. and I could see the number of rows under rows column of the result.

ssrp
  • 1,126
  • 5
  • 17
  • 35
  • For my current set up, this is by far the fastest and provides a sufficiently accurate row count for my needs! – gvee Sep 01 '20 at 15:08
4

Perhaps you may want to consider doing a SELECT max(Id) - min(Id) + 1. This will only work if your Ids are sequential and rows are not deleted. It is however very fast.

sky-dev
  • 6,190
  • 2
  • 33
  • 32
  • 5
    Be careful: servers sometimes use auto increment value > 1 (for backup reasons), so this solution is good but you should check your DB configuration first. – Alex Nov 16 '14 at 15:55
4

This is the best query able to get the fastest results.

SELECT SQL_CALC_FOUND_ROWS 1 FROM `orders`;
SELECT FOUND_ROWS();

In my benchmark test: 0.448s

enter image description here

This query takes 4.835s

SELECT SQL_CALC_FOUND_ROWS * FROM `orders`;
SELECT FOUND_ROWS();

enter image description here

count * takes 25.675s

SELECT count(*) FROM `orders`;

enter image description here

X zheng
  • 1,731
  • 1
  • 17
  • 25
2

If you don't need super-exact count, then you can set a lower transaction isolation level for the current session. Do it like this:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT count(*) FROM the_table WHERE ...;
COMMIT; /* close the transaction */

It is also good to have an index that matches the WHERE condition.

It really speeds up the counting for big InnoDB tables. I checked it on a table with ~700M rows and heavy load, it works. It reduced the query time from ~451 seconds to ~2 seconds.

I took the idea from this answer: https://stackoverflow.com/a/918092/1743367

JustAC0der
  • 2,871
  • 3
  • 32
  • 35
  • 1
    As `READ_UNCOMMITTED` suggests this way your counts may be not exactly precise and you'll see the [dirty read](https://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_dirty_read) effects. For example if you are trying to `COUNT(*).. WHERE is_maked_for_deletion = FALSE` and another transaction it trying to mark lots of rows for deletion simultaneously, your counts will see some of these markings even if that changing transaction is going to be rolled back. So the suggested approach is great if you just want the rough counts and nobody is likely to try changing MANY records simultaneously. – Artem Apr 21 '21 at 09:12
  • If you're using AWS Aurora and making a query to a read-only replica, this method won't work. "These DB instances ignore any SET TRANSACTION ISOLATION LEVEL statements and continue using the REPEATABLE READ isolation level." (source: https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraMySQL.Reference.html#AuroraMySQL.Reference.IsolationLevels ) – JustAC0der May 31 '21 at 08:38
0

A count(*) statement with a where condition on the primary key returned the row count much faster for me avoiding full table scan.

SELECT COUNT(*) FROM ... WHERE <PRIMARY_KEY> IS NOT NULL;

This was much faster for me than

SELECT COUNT(*) FROM ...
ayakout
  • 49
  • 7
-1

I handled tables for the German Government with sometimes 60 million records.

And we needed to know many times the total rows.

So we database programmers decided that in every table is record one always the record in which the total record numbers is stored. We updated this number, depending on INSERT or DELETE rows.

We tried all other ways. This is by far the fastest way.

  • 2
    and what are the details of how you updated that row? Whichs means though a faulty design to a table, where all rows would require a wasted int to come along for the ride. – Drew Oct 01 '16 at 21:53