Is there a way to get the count of rows in all tables in a MySQL database without running a SELECT count()
on each table?
-
3Extended answer that is also accurate for InnoDB: http://stackoverflow.com/questions/24707814/mysql-summarize-all-table-row-counts-in-a-single-query – gwideman Jul 11 '14 at 23:53
24 Answers
SELECT SUM(TABLE_ROWS)
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = '{your_db}';
Note from the docs though: For InnoDB tables, the row count is only a rough estimate used in SQL optimization. You'll need to use COUNT(*) for exact counts (which is more expensive).
-
313or, if you want for each table: SELECT table_name, TABLE_ROWS FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '{your_db}'; – TheSoftwareJedi Nov 13 '08 at 02:03
-
8Is there any other way to get table_row and table_name ? Because i want exact result not rough estimate. Thank you. – krunal shah Apr 01 '11 at 03:35
-
3@krunalshah, This is one of the restrictions of InnoDB. See http://dev.mysql.com/doc/refman/5.0/en/innodb-restrictions.html, section Restrictions on InnoDB Tables, for more info. You could always use a SELECT COUNT(*) FROM t, which however, is a lot slower – Marking Feb 23 '12 at 11:21
-
@Marking I'm pretty sure doing a `SELECT COUNT(*) ...` is still subject to the count estimation – JamesHalsall Mar 15 '13 at 12:52
-
2Jaitsu, no it's not. count(*) (or more realistically count(id)) is what mysql uses to count it's rows isn't it? In any case, I just tested it and got a bigger number for the count() call, whatever that is worth. – codygman May 24 '13 at 16:24
-
2SELECT TABLE_NAME, SUM(TABLE_ROWS) N FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '{your_db}' group by TABLE_NAME; – PiyusG Nov 17 '14 at 06:48
-
2Where exactly does the "COUNT(*)" Fit in to the above answer? I would like exact result. – Mar 14 '15 at 19:02
-
Why in my case, the TABLE_ROWS is inconsistent for every query? I am using mariadb 10 – Fandi Susanto Oct 07 '16 at 09:15
-
For me, `SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_ROWS FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '{your_db}' ORDER BY TABLE_ROWS DESC;` was helpful, but I wish I knew how to get exact counts instead of just an estimate. – Ryan Jun 24 '21 at 18:18
-
I would rather suggest this: SELECT table_schema,table_name, table_rows FROM INFORMATION_SCHEMA.TABLES order by table_name; – Alberto Soto Nov 23 '21 at 02:03
You can probably put something together with Tables table. I've never done it, but it looks like it has a column for TABLE_ROWS and one for TABLE NAME.
To get rows per table, you can use a query like this:
SELECT table_name, table_rows
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = '**YOUR SCHEMA**';

- 22,558
- 8
- 42
- 71
-
7Is there any other way to get table_row and table_name ? Because i want exact result not rough estimate. Thank you. – krunal shah Apr 01 '11 at 03:35
-
2as kuranl mentioned this returns just an estimate and will probably return different results when run a couple of times – Kris Dec 12 '12 at 12:12
-
1Tables with at least ~250 records seem to report a different number of rows each time I run this query. – Arthur Aug 01 '13 at 14:25
-
2Ooops... wish I would have seen the word "Estimated" before hand... like yesterday! Shouldn't answer be rejected? As OP did not ask for "estimated" and it seems silly to think that he may want an estimate. "estimate"Could save maroons like me from missing the "estimate"? – Dec 30 '15 at 05:06
Like @Venkatramanan and others I found INFORMATION_SCHEMA.TABLES unreliable (using InnoDB, MySQL 5.1.44), giving different row counts each time I run it even on quiesced tables. Here's a relatively hacky (but flexible/adaptable) way of generating a big SQL statement you can paste into a new query, without installing Ruby gems and stuff.
SELECT CONCAT(
'SELECT "',
table_name,
'" AS table_name, COUNT(*) AS exact_row_count FROM `',
table_schema,
'`.`',
table_name,
'` UNION '
)
FROM INFORMATION_SCHEMA.TABLES
WHERE table_schema = '**my_schema**';
It produces output like this:
SELECT "func" AS table_name, COUNT(*) AS exact_row_count FROM my_schema.func UNION
SELECT "general_log" AS table_name, COUNT(*) AS exact_row_count FROM my_schema.general_log UNION
SELECT "help_category" AS table_name, COUNT(*) AS exact_row_count FROM my_schema.help_category UNION
SELECT "help_keyword" AS table_name, COUNT(*) AS exact_row_count FROM my_schema.help_keyword UNION
SELECT "help_relation" AS table_name, COUNT(*) AS exact_row_count FROM my_schema.help_relation UNION
SELECT "help_topic" AS table_name, COUNT(*) AS exact_row_count FROM my_schema.help_topic UNION
SELECT "host" AS table_name, COUNT(*) AS exact_row_count FROM my_schema.host UNION
SELECT "ndb_binlog_index" AS table_name, COUNT(*) AS exact_row_count FROM my_schema.ndb_binlog_index UNION
Copy and paste except for the last UNION to get nice output like,
+------------------+-----------------+
| table_name | exact_row_count |
+------------------+-----------------+
| func | 0 |
| general_log | 0 |
| help_category | 37 |
| help_keyword | 450 |
| help_relation | 990 |
| help_topic | 504 |
| host | 0 |
| ndb_binlog_index | 0 |
+------------------+-----------------+
8 rows in set (0.01 sec)

- 9,668
- 3
- 56
- 48

- 3,842
- 1
- 26
- 31
-
3Thanks, I was hoping I wouldn't have to install any plugins/gems to get exact counts. – bradvido Sep 02 '14 at 20:02
-
1Takes too long to execute in case of large number of tables in the database. – ollamh May 15 '18 at 07:13
-
1add "select * from (" at beginning and ") as output order by exact_row_count desc" at the end of generated query after removing last UNION to get order by table count desc – Raghavendra Jun 22 '18 at 05:42
-
1To exclude views: WHERE table_schema = '**my_schema**' and TABLE_TYPE LIKE '%TABLE%' – Watson Jul 27 '19 at 00:42
-
2
I just run:
show table status;
This will give you the row count for EVERY table plus a bunch of other info. I used to use the selected answer above, but this is much easier.
I'm not sure if this works with all versions, but I'm using 5.5 with InnoDB engine.

- 11,762
- 9
- 46
- 64
-
10Unfortunately, if you're using InnoDB, this approach suffers from the same inaccuracies as the other methods described above. For example, I have an InnoDB table that has approximately 65,000 rows, yet these methods here report that it has anywhere from 350,000 to over 780,000. – PeterToTheThird Mar 12 '14 at 01:53
-
For a DB with few rows, it's fairly accurate (or, accurate enough for my needs). It gave me 1086 rows for a table that COUNT(*) reported 904 rows. – Magne Dec 10 '14 at 08:58
-
By far best answer. I use InnoDB but I only need a quick command to know the order of magnitude. – Nemo Jan 10 '16 at 21:03
-
Seriously, wish this was accepted. Not using InnoDB and gives me exact answer. – Kellen Stuart Feb 25 '16 at 04:37
-
This grabs the TABLE_ROWS column from INFORMATION_SCHEMA.TABLES table. Please note that this info (given by `show table status` or by TABLE_ROWS column) is only a rough estimate which may not be accurate for InnoDB (I have not tested yet MyISAM). – Eugen Mihailescu May 08 '16 at 18:50
-
2Rows number is not accurate but "Auto_increment" can give you accurate number if you didn't delete any rows from such tables. – Peter T. Aug 22 '16 at 11:01
Simple way:
SELECT
TABLE_NAME, SUM(TABLE_ROWS)
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = '{Your_DB}'
GROUP BY TABLE_NAME;
Result example:
+----------------+-----------------+
| TABLE_NAME | SUM(TABLE_ROWS) |
+----------------+-----------------+
| calls | 7533 |
| courses | 179 |
| course_modules | 298 |
| departments | 58 |
| faculties | 236 |
| modules | 169 |
| searches | 25423 |
| sections | 532 |
| universities | 57 |
| users | 10293 |
+----------------+-----------------+

- 17,828
- 6
- 117
- 94
SELECT TABLE_NAME,SUM(TABLE_ROWS)
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'your_db'
GROUP BY TABLE_NAME;
That's all you need.

- 3,093
- 5
- 35
- 42

- 177
- 1
- 2
-
1
-
1that's the best answer actually! Also the simpler to be executed from mysql cli: `mysql> SELECT TABLE_NAME,SUM(TABLE_ROWS) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'ngramsdb' GROUP BY TABLE_NAME;` – loretoparisi Mar 20 '18 at 09:33
This stored procedure lists tables, counts records, and produces a total number of records at the end.
To run it after adding this procedure:
CALL `COUNT_ALL_RECORDS_BY_TABLE` ();
-
The Procedure:
DELIMITER $$
CREATE DEFINER=`root`@`127.0.0.1` PROCEDURE `COUNT_ALL_RECORDS_BY_TABLE`()
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE TNAME CHAR(255);
DECLARE table_names CURSOR for
SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE();
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN table_names;
DROP TABLE IF EXISTS TCOUNTS;
CREATE TEMPORARY TABLE TCOUNTS
(
TABLE_NAME CHAR(255),
RECORD_COUNT INT
) ENGINE = MEMORY;
WHILE done = 0 DO
FETCH NEXT FROM table_names INTO TNAME;
IF done = 0 THEN
SET @SQL_TXT = CONCAT("INSERT INTO TCOUNTS(SELECT '" , TNAME , "' AS TABLE_NAME, COUNT(*) AS RECORD_COUNT FROM ", TNAME, ")");
PREPARE stmt_name FROM @SQL_TXT;
EXECUTE stmt_name;
DEALLOCATE PREPARE stmt_name;
END IF;
END WHILE;
CLOSE table_names;
SELECT * FROM TCOUNTS;
SELECT SUM(RECORD_COUNT) AS TOTAL_DATABASE_RECORD_CT FROM TCOUNTS;
END

- 7,053
- 1
- 31
- 36

- 155
- 1
- 2
-
This is what I was looking for, nice. How can I add a Where clause in the user table query query, I mean where you are taking the count of rows in the tables. – Iftekhar Ilm May 02 '23 at 12:24
There's a bit of a hack/workaround to this estimate problem.
Auto_Increment - for some reason this returns a much more accurate row count for your database if you have auto increment set up on tables.
Found this when exploring why show table info did not match up with the actual data.
SELECT
table_schema 'Database',
SUM(data_length + index_length) AS 'DBSize',
SUM(TABLE_ROWS) AS DBRows,
SUM(AUTO_INCREMENT) AS DBAutoIncCount
FROM information_schema.tables
GROUP BY table_schema;
+--------------------+-----------+---------+----------------+
| Database | DBSize | DBRows | DBAutoIncCount |
+--------------------+-----------+---------+----------------+
| Core | 35241984 | 76057 | 8341 |
| information_schema | 163840 | NULL | NULL |
| jspServ | 49152 | 11 | 856 |
| mysql | 7069265 | 30023 | 1 |
| net_snmp | 47415296 | 95123 | 324 |
| performance_schema | 0 | 1395326 | NULL |
| sys | 16384 | 6 | NULL |
| WebCal | 655360 | 2809 | NULL |
| WxObs | 494256128 | 530533 | 3066752 |
+--------------------+-----------+---------+----------------+
9 rows in set (0.40 sec)
You could then easily use PHP or whatever to return the max of the 2 data columns to give the "best estimate" for row count.
i.e.
SELECT
table_schema 'Database',
SUM(data_length + index_length) AS 'DBSize',
GREATEST(SUM(TABLE_ROWS), SUM(AUTO_INCREMENT)) AS DBRows
FROM information_schema.tables
GROUP BY table_schema;
Auto Increment will always be +1 * (table count) rows off, but even with 4,000 tables and 3 million rows, that's 99.9% accurate. Much better than the estimated rows.
The beauty of this is that the row counts returned in performance_schema are erased for you, as well, because greatest does not work on nulls. This may be an issue if you have no tables with auto increment, though.

- 603
- 2
- 7
- 19
One more option: for non InnoDB it uses data from information_schema.TABLES (as it's faster), for InnoDB - select count(*) to get the accurate count. Also it ignores views.
SET @table_schema = DATABASE();
-- or SET @table_schema = 'my_db_name';
SET GROUP_CONCAT_MAX_LEN=131072;
SET @selects = NULL;
SELECT GROUP_CONCAT(
'SELECT "', table_name,'" as TABLE_NAME, COUNT(*) as TABLE_ROWS FROM `', table_name, '`'
SEPARATOR '\nUNION\n') INTO @selects
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = @table_schema
AND ENGINE = 'InnoDB'
AND TABLE_TYPE = "BASE TABLE";
SELECT CONCAT_WS('\nUNION\n',
CONCAT('SELECT TABLE_NAME, TABLE_ROWS FROM information_schema.TABLES WHERE TABLE_SCHEMA = ? AND ENGINE <> "InnoDB" AND TABLE_TYPE = "BASE TABLE"'),
@selects) INTO @selects;
PREPARE stmt FROM @selects;
EXECUTE stmt USING @table_schema;
DEALLOCATE PREPARE stmt;
If your database has a lot of big InnoDB tables counting all rows can take more time.

- 1,666
- 1
- 9
- 20
-
I added a line with `SELECT CONCAT(@selects, ' ORDER BY TABLE_ROWS ') INTO @selects;` before the `PREPARE` so as to get a more readable output, but this is the best answer to me. – Balmipour Jul 13 '20 at 22:32
You can try this. It is working fine for me.
SELECT IFNULL(table_schema,'Total') "Database",TableCount
FROM (SELECT COUNT(1) TableCount,table_schema
FROM information_schema.tables
WHERE table_schema NOT IN ('information_schema','mysql')
GROUP BY table_schema WITH ROLLUP) A;
To see the record counts for the current DB that's in use:
SELECT table_name, TABLE_ROWS FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE();

- 66,836
- 64
- 257
- 336
If you use the database information_schema, you can use this mysql code (the where part makes the query not show tables that have a null value for rows):
SELECT TABLE_NAME, TABLE_ROWS
FROM `TABLES`
WHERE `TABLE_ROWS` >=0

- 2,162
- 2
- 25
- 30
The following query produces a(nother) query that will get the value of count(*) for every table, from every schema, listed in information_schema.tables. The entire result of the query shown here - all rows taken together - comprise a valid SQL statement ending in a semicolon - no dangling 'union'. The dangling union is avoided by use of a union in the query below.
select concat('select "', table_schema, '.', table_name, '" as `schema.table`,
count(*)
from ', table_schema, '.', table_name, ' union ') as 'Query Row'
from information_schema.tables
union
select '(select null, null limit 0);';

- 6,476
- 10
- 48
- 89

- 11
- 1
This is what I do to get the actual count (no using the schema)
It's slower but more accurate.
It's a two step process at
Get list of tables for your db. You can get it using
mysql -uroot -p mydb -e "show tables"
Create and assign the list of tables to the array variable in this bash script (separated by a single space just like in the code below)
array=( table1 table2 table3 ) for i in "${array[@]}" do echo $i mysql -uroot mydb -e "select count(*) from $i" done
Run it:
chmod +x script.sh; ./script.sh
Most other answers suggest using INFORMATION_SCHEMA.TABLES
, but in MySQL 8 it no longer exists. Rows count has been moved to INFORMATION_SCHEMA.INNODB_TABLESTATS
.
You can query it with:
SELECT *
FROM information_schema.INNODB_TABLESTATS
WHERE NAME LIKE "YOUR_DB_NAME/%"
ORDER BY NUM_ROWS DESC
Note that it's still an approximation like before, not an exact count.

- 1,116
- 13
- 18
This is how I count TABLES and ALL RECORDS using PHP:
$dtb = mysql_query("SHOW TABLES") or die (mysql_error());
$jmltbl = 0;
$jml_record = 0;
$jml_record = 0;
while ($row = mysql_fetch_array($dtb)) {
$sql1 = mysql_query("SELECT * FROM " . $row[0]);
$jml_record = mysql_num_rows($sql1);
echo "Table: " . $row[0] . ": " . $jml_record record . "<br>";
$jmltbl++;
$jml_record += $jml_record;
}
echo "--------------------------------<br>$jmltbl Tables, $jml_record > records.";

- 1,239
- 8
- 15

- 11
Poster wanted row counts without counting, but didn't specify which table engine. With InnoDB, I only know one way, which is to count.
This is how I pick my potatoes:
# Put this function in your bash and call with:
# rowpicker DBUSER DBPASS DBNAME [TABLEPATTERN]
function rowpicker() {
UN=$1
PW=$2
DB=$3
if [ ! -z "$4" ]; then
PAT="LIKE '$4'"
tot=-2
else
PAT=""
tot=-1
fi
for t in `mysql -u "$UN" -p"$PW" "$DB" -e "SHOW TABLES $PAT"`;do
if [ $tot -lt 0 ]; then
echo "Skipping $t";
let "tot += 1";
else
c=`mysql -u "$UN" -p"$PW" "$DB" -e "SELECT count(*) FROM $t"`;
c=`echo $c | cut -d " " -f 2`;
echo "$t: $c";
let "tot += c";
fi;
done;
echo "total rows: $tot"
}
I am making no assertions about this other than that this is a really ugly but effective way to get how many rows exist in each table in the database regardless of table engine and without having to have permission to install stored procedures, and without needing to install ruby or php. Yes, its rusty. Yes it counts. count(*) is accurate.

- 1,081
- 8
- 8
Based on @Nathan's answer above, but without needing to "remove the final union" and with the option to sort the output, I use the following SQL. It generates another SQL statement which then just run:
select CONCAT( 'select * from (\n', group_concat( single_select SEPARATOR ' UNION\n'), '\n ) Q order by Q.exact_row_count desc') as sql_query
from (
SELECT CONCAT(
'SELECT "',
table_name,
'" AS table_name, COUNT(1) AS exact_row_count
FROM `',
table_schema,
'`.`',
table_name,
'`'
) as single_select
FROM INFORMATION_SCHEMA.TABLES
WHERE table_schema = 'YOUR_SCHEMA_NAME'
and table_type = 'BASE TABLE'
) Q
You do need a sufficiently large value of group_concat_max_len
server variable but from MariaDb 10.2.4 it should default to 1M.

- 6,539
- 3
- 39
- 65
I don't know why this has to be so hard but that's life. Here's my bash script that performs actual counts. Just save this as (e.g. count_rows.sh ), make it executable (e.g. chmod 755 count_rows.sh ), and run it (e.g. ./count_rows.sh )
#!/bin/bash
readarray -t TABLES < <(mysql --skip-column-names -u myuser -pmypassword mydbname -e "show tables")
# now we have an array like:
# TABLES='([0]="customer" [1]="order" [2]="product")'
# You can print out the array with:
#declare -p TABLES
for i in "${TABLES[@]}"
do
#echo $i
COUNT=$(mysql --skip-column-names -u username -pmypassword mydbname -e "select count(*) from $i")
echo $i : $COUNT
done

- 173
- 1
- 7
Like many others, I have difficulty getting an accurate value on the INFORMATION_SCHEMA
tables with InnoDB, and would infinitely benefit from being able to make a query that depends on count()
, and, hopefully, do it in one, single query.
First, make sure to enable massive group_concats:
SET SESSION group_concat_max_len = 1000000;
Then run this query to get the resultant query you'll run for your database.
SELECT CONCAT('SELECT ', GROUP_CONCAT(table1.count SEPARATOR ',\n')) FROM (
SELECT concat('(SELECT count(id) AS \'',table_name,' Count\' ','FROM ',table_name,') AS ',table_name,'_Count') AS 'count'
FROM information_schema.tables
WHERE table_schema = '**YOUR_DATABASE_HERE**'
) AS table1
This will generate output such as...
SELECT (SELECT count(id) AS 'table1 Count' FROM table1) AS table1_Count,
(SELECT count(id) AS 'table2 Count' FROM table2) AS table2_Count,
(SELECT count(id) AS 'table3 Count' FROM table3) AS table3_Count;
This in turn gave the following results:
*************************** 1. row ***************************
table1_Count: 1
table2_Count: 1
table3_Count: 0

- 18,769
- 10
- 104
- 133
This is my proposal, pretty clean
SELECT table_name, table_rows FROM information_schema.tables where
information_schema.tables.TABLE_SCHEMA="your_schema_name";

- 2,286
- 1
- 24
- 20
The code below generation the select query for all tales. Just delete last "UNION ALL" select all result and paste a new query window to run.
SELECT
concat('select ''', table_name ,''' as TableName, COUNT(*) as RowCount from ' , table_name , ' UNION ALL ') as TR FROM
information_schema.tables where
table_schema = 'Database Name'

- 91
- 1
- 4
If you want the exact numbers, use the following ruby script. You need Ruby and RubyGems.
Install following Gems:
$> gem install dbi
$> gem install dbd-mysql
File: count_table_records.rb
require 'rubygems'
require 'dbi'
db_handler = DBI.connect('DBI:Mysql:database_name:localhost', 'username', 'password')
# Collect all Tables
sql_1 = db_handler.prepare('SHOW tables;')
sql_1.execute
tables = sql_1.map { |row| row[0]}
sql_1.finish
tables.each do |table_name|
sql_2 = db_handler.prepare("SELECT count(*) FROM #{table_name};")
sql_2.execute
sql_2.each do |row|
puts "Table #{table_name} has #{row[0]} rows."
end
sql_2.finish
end
db_handler.disconnect
Go back to the command-line:
$> ruby count_table_records.rb
Output:
Table users has 7328974 rows.

- 187
- 1
- 4
If you know the number of tables and their names, and assuming they each have primary keys, you can use a cross join in combination with COUNT(distinct [column])
to get the rows that come from each table:
SELECT
COUNT(distinct t1.id) +
COUNT(distinct t2.id) +
COUNT(distinct t3.id) AS totalRows
FROM firstTable t1, secondTable t2, thirdTable t3;
Here is an SQL Fiddle example.

- 16,492
- 10
- 71
- 133