I have a largish but narrow InnoDB table with ~9m records. Doing count(*)
or count(id)
on the table is extremely slow (6+ seconds):
For the time being I've solved the problem by using this approximation:
EXPLAIN SELECT COUNT(id) FROM data USE INDEX (PRIMARY)
The approximate number of rows can be read from the rows
column of the explain plan when using InnoDB as shown above. When using MyISAM this will remain EMPTY as the table reference isbeing optimized away- so if empty fallback to traditional SELECT COUNT
instead.
As of MySQL 5.1.6 you can use the Event Scheduler and insert the count to a stats table regularly.
First create a table to hold the count:
CREATE TABLE stats (
`key` varchar(50) NOT NULL PRIMARY KEY,
`value` varchar(100) NOT NULL);
Then create an event to update the table:
CREATE EVENT update_stats
ON SCHEDULE
EVERY 5 MINUTE
DO
INSERT INTO stats (`key`, `value`)
VALUES ('data_count', (select count(id) from data))
ON DUPLICATE KEY UPDATE value=VALUES(value);
It's not perfect but it offers a self contained solution (no cronjob or queue) that can be easily tailored to run as often as the required freshness of the count.
Based on @Che code, you can also use triggers on INSERT and on UPDATE to perf2 in order to keep the value in stats table up to date.
CREATE TRIGGER `count_up` AFTER INSERT ON `perf2` FOR EACH ROW UPDATE `stats`
SET
`stats`.`value` = `stats`.`value` + 1
WHERE
`stats`.`key` = "perf2_count";
CREATE TRIGGER `count_down` AFTER DELETE ON `perf2` FOR EACH ROW UPDATE `stats`
SET
`stats`.`value` = `stats`.`value` - 1
WHERE
`stats`.`key` = "perf2_count";
This would have the advantage of eliminating the performance issue of performing a count(*) and would only be executed when data changes in table perf2
select max(id) - min(id) from xxx_table where
....
This will use "Select tables optimized away", and is very fast!!!
Note max(id) - min(id)
is actually bigger than count(1)
.