Limiting Table Row Count in MySQL: Trigger, Partition, and Tablespace Solutions
This article explores multiple MySQL techniques—triggers with a counter table, range partitioning, general tablespace limits, and application‑side buffering—to enforce a hard cap of 10,000 rows per table, discussing implementation steps, code examples, and the trade‑offs of each method.
Background
The author was asked by a client whether a single MySQL table could be limited to a fixed number of records (e.g., 10,000 rows) so that further inserts are rejected or cause an error.
Main Content
There are generally two categories of solutions: one implemented on the database side and the other on the application side.
Database‑side solutions (hard limit of 10,000 rows)
1. Trigger solution
The idea is to use a trigger that checks a counter table before each insert; if the limit is reached, the oldest row is deleted (or inserted then deleted) to keep the total at the limit.
mysql:ytt_new>create table t1(id int auto_increment primary key, r1 int);
Query OK, 0 rows affected (0.06 sec)
mysql:ytt_new>create table t1_count(cnt smallint unsigned);
Query OK, 0 rows affected (0.04 sec)
mysql:ytt_new>insert t1_count set cnt=0;
Query OK, 1 row affected (0.11 sec)Insert‑trigger to increment the counter:
DELIMITER $$
USE `ytt_new`$$
DROP TRIGGER IF EXISTS `tr_t1_insert`$$
CREATE TRIGGER `tr_t1_insert` AFTER INSERT ON `t1`
FOR EACH ROW BEGIN
UPDATE t1_count SET cnt = cnt + 1;
END$$
DELIMITER ;Delete‑trigger to decrement the counter:
DELIMITER $$
USE `ytt_new`$$
DROP TRIGGER IF EXISTS `tr_t1_delete`$$
CREATE TRIGGER `tr_t1_delete` AFTER DELETE ON `t1`
FOR EACH ROW BEGIN
UPDATE t1_count SET cnt = cnt - 1;
END$$
DELIMITER ;Populate the table with 10,000 rows for testing:
mysql:ytt_new>insert t1 (r1) with recursive tmp(a,b) as (select 1,1 union all select a+1,ceil(rand()*20) from tmp where a<10000 ) select b from tmp;
Query OK, 10000 rows affected (0.68 sec)Check the counter:
mysql:ytt_new>select cnt from t1_count;
+-------+
| cnt |
+-------+
| 10000 |
+-------+A stored procedure can encapsulate the logic, deleting the oldest row when the limit is reached before inserting a new one:
DELIMITER $$
USE `ytt_new`$$
DROP PROCEDURE IF EXISTS `sp_insert_t1`$$
CREATE PROCEDURE `sp_insert_t1`(IN f_r1 INT)
BEGIN
DECLARE v_cnt INT DEFAULT 0;
SELECT cnt INTO v_cnt FROM t1_count;
IF v_cnt >= 10000 THEN
DELETE FROM t1 ORDER BY id ASC LIMIT 1;
END IF;
INSERT INTO t1(r1) VALUES (f_r1);
END$$
DELIMITER ;Calling the procedure keeps the row count at 10,000:
mysql:ytt_new>call sp_insert_t1(9999);
Query OK, 1 row affected (0.02 sec)
mysql:ytt_new>select count(*) from t1;
+----------+
| count(*) |
+----------+
| 10000 |
+----------+Drawbacks of the trigger approach include: counter desynchronization on TRUNCATE, need to recreate triggers after DROP, and the requirement that all inserts go through the stored procedure.
2. Partition table solution
Create a RANGE partition where the first partition holds up to 10,000 rows; when full, drop that partition and reorganize the table.
mysql:ytt_new>create table t1(id int auto_increment primary key, r1 int) partition by range(id) (partition p1 values less than(10001), partition p_max values less than(maxvalue));
Query OK, 0 rows affected (0.45 sec)Check if the first partition is full:
mysql:ytt_new>select count(*) from t1 partition(p1);
+----------+
| count(*) |
+----------+
| 10000 |
+----------+Drop the full partition and reorganize:
mysql:ytt_new>alter table t1 drop partition p1;
Query OK, 0 rows affected (0.06 sec)
mysql:ytt_new>alter table t1 reorganize partition p_max into (partition p1 values less than (20001), partition p_max values less than (maxvalue));
Query OK, 0 rows affected (0.60 sec)Advantages: inserts can come from any source, and dropping a partition is fast. Disadvantages: gaps in the primary key require redefining partitions.
3. General tablespace solution
Pre‑calculate the disk space needed for 10,000 rows, create a dedicated tablespace, and assign the table to it. When the tablespace reports “TABLE IS FULL”, stop inserts.
mysql:ytt_new>create tablespace ts1 add datafile '/tmp/mysql/ts1.ibd' engine innodb;
Query OK, 0 rows affected (0.11 sec)
mysql:ytt_new>alter table t1 tablespace ts1;
Query OK, 0 rows affected (0.12 sec)Attempting to insert after the limit triggers an error:
mysql:ytt_new>insert t1 (r1) values (200);
ERROR 1114 (HY000): The table 't1' is fullTo reuse the table, drop the tablespace and truncate the table:
mysql:ytt_new>alter table t1 tablespace innodb_file_per_table;
Query OK, 0 rows affected (0.18 sec)
mysql:ytt_new>drop tablespace ts1;
Query OK, 0 rows affected (0.13 sec)
mysql:ytt_new>truncate table t1;
Query OK, 0 rows affected (0.04 sec)Application‑side solution
Cache incoming rows in a file (e.g., t1.csv); when the file reaches 10,000 lines, truncate the table on the DB side and bulk‑load the file.
Conclusion
In the MyISAM era MySQL offered a max_rows table attribute, but it was only advisory. InnoDB provides no built‑in hard limit, so the approaches above—triggers with a counter table, range partitioning, dedicated tablespaces, or application‑side buffering—are the practical ways to enforce a fixed row count, with the choice depending on specific requirements.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Aikesheng Open Source Community
The Aikesheng Open Source Community provides stable, enterprise‑grade MySQL open‑source tools and services, releases a premium open‑source component each year (1024), and continuously operates and maintains them.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
