MySQL Auto Increment
AUTO_INCREMENT makes MySQL generate a new number for each row you insert: 1, 2, 3 and so on. It is the standard way to create primary keys, so you never have to work out the next ID yourself, even when many users insert at the same time.
1. Creating an AUTO_INCREMENT Column
CREATE TABLE members (
member_id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(40) NOT NULL,
joined_on DATE NOT NULL DEFAULT (CURRENT_DATE)
);
INSERT INTO members (name) VALUES ('Aarti'), ('Bhavesh'), ('Celine');
SELECT member_id, name FROM members;
Result: 3 rows
| member_id | name |
|---|---|
| 1 | Aarti |
| 2 | Bhavesh |
| 3 | Celine |
- Only one AUTO_INCREMENT column per table, and it must be indexed (normally the primary key).
- Use an integer type;
UNSIGNEDdoubles the positive range. - Leaving the column out, or inserting NULL or 0, generates the next value.
2. Inserting Rows
INSERT INTO members (member_id, name) VALUES (NULL, 'Dev'); -- NULL → generated
INSERT INTO members (name) VALUES ('Esha'); -- omitted → generated
INSERT INTO members (member_id, name) VALUES (100, 'Faraz'); -- explicit value is used
INSERT INTO members (name) VALUES ('Gauri'); -- continues after 100
SELECT member_id, name FROM members;
Result: 7 rows
| member_id | name |
|---|---|
| 1 | Aarti |
| 2 | Bhavesh |
| 3 | Celine |
| 4 | Dev |
| 5 | Esha |
| 100 | Faraz |
| 101 | Gauri |
3. Getting the New ID: LAST_INSERT_ID()
INSERT INTO members (name) VALUES ('Harsh');
SELECT LAST_INSERT_ID() AS harsh_id;
INSERT INTO members (name) VALUES ('Ira'), ('Jatin'), ('Kavya');
SELECT LAST_INSERT_ID() AS first_id_of_batch;
Result: 1 row
| harsh_id |
|---|
| 102 |
Result: 1 row
| first_id_of_batch |
|---|
| 103 |
- It is per connection: other users' inserts never change your value.
- After a multi-row insert it returns the first generated ID.
- In Java use
Statement.RETURN_GENERATED_KEYSandgetGeneratedKeys(); JPA does this for@GeneratedValue(strategy = GenerationType.IDENTITY).
4. Why Gaps Appear
DELETE FROM members WHERE name = 'Bhavesh';
START TRANSACTION;
INSERT INTO members (name) VALUES ('Rolled Back');
ROLLBACK;
INSERT INTO members (name) VALUES ('Lalit');
SELECT member_id, name FROM members WHERE member_id > 100;
Result: 6 rows
| member_id | name |
|---|---|
| 101 | Gauri |
| 102 | Harsh |
| 103 | Ira |
| 104 | Jatin |
| 105 | Kavya |
| 107 | Lalit |
The rolled-back insert consumed an ID that was never used. MySQL never reuses numbers, which keeps inserts fast and safe under concurrency. Never rely on IDs being consecutive.
5. Setting the Start Value
CREATE TABLE invoices (
invoice_no INT AUTO_INCREMENT PRIMARY KEY,
amount DECIMAL(9,2)
) AUTO_INCREMENT = 5001;
INSERT INTO invoices (amount) VALUES (1200), (450);
SELECT * FROM invoices;
Result: 2 rows
| invoice_no | amount |
|---|---|
| 5001 | 1200.00 |
| 5002 | 450.00 |
Change the next value later with ALTER TABLE. You can't set it below the current maximum (MySQL quietly uses max + 1 instead):
ALTER TABLE invoices AUTO_INCREMENT = 9000;
INSERT INTO invoices (amount) VALUES (780);
SELECT invoice_no FROM invoices ORDER BY invoice_no DESC LIMIT 1;
Result: 1 row
| invoice_no |
|---|
| 9000 |
6. Checking the Next Value
SELECT table_name, auto_increment
FROM information_schema.tables
WHERE table_schema = 'library_app';
Result: 2 rows
| TABLE_NAME | AUTO_INCREMENT |
|---|---|
| invoices | 9001 |
| members | 108 |
ANALYZE TABLE or set information_schema_stats_expiry = 0 for an up-to-date number.7. Resetting the Counter
TRUNCATE TABLE empties the table and resets the counter to 1; DELETE does not:
CREATE TABLE temp_ids (id INT AUTO_INCREMENT PRIMARY KEY, v CHAR(1));
INSERT INTO temp_ids (v) VALUES ('a'), ('b'), ('c');
DELETE FROM temp_ids;
INSERT INTO temp_ids (v) VALUES ('d');
SELECT id FROM temp_ids;
TRUNCATE TABLE temp_ids;
INSERT INTO temp_ids (v) VALUES ('e');
SELECT id FROM temp_ids;
After DELETE: continues
| id |
|---|
| 4 |
After TRUNCATE: starts again
| id |
|---|
| 1 |
8. Step and Offset
The system variables auto_increment_increment (step) and auto_increment_offset (start) are mostly used in multi-source replication, so two servers never generate the same ID (one makes odd numbers, the other even):
SET SESSION auto_increment_increment = 2, auto_increment_offset = 1;
CREATE TABLE odd_ids (id INT AUTO_INCREMENT PRIMARY KEY, v CHAR(1));
INSERT INTO odd_ids (v) VALUES ('a'), ('b'), ('c');
SELECT id FROM odd_ids;
SET SESSION auto_increment_increment = 1, auto_increment_offset = 1;
Result: 3 rows
| id |
|---|
| 1 |
| 3 |
| 5 |
9. Running Out of Numbers
When the column's maximum is reached, inserts fail. With TINYINT that's quick to show:
CREATE TABLE tiny_ids (id TINYINT AUTO_INCREMENT PRIMARY KEY, v CHAR(1)) AUTO_INCREMENT = 127;
INSERT INTO tiny_ids (v) VALUES ('x');
INSERT INTO tiny_ids (v) VALUES ('y');
Query OK, 0 rows affected
Query OK, 1 row affected
ERROR 1062 (23000): Duplicate entry '127' for key 'tiny_ids.PRIMARY'
Once the counter is at the type's maximum, MySQL keeps generating that same maximum, so the next insert fails with a duplicate-key error.
| Type | Max (signed) | Max (UNSIGNED) |
|---|---|---|
| INT | 2,147,483,647 | 4,294,967,295 |
| BIGINT | 9.2 × 1018 | 1.8 × 1019 |
BIGINT UNSIGNED for tables that may receive billions of rows, such as events or logs.10. Summary
AUTO_INCREMENTgenerates increasing integer IDs; one per table, indexed.- Omit the column (or pass NULL) to get the next value;
LAST_INSERT_ID()returns it. - Gaps from deletes and rollbacks are normal; never reuse IDs.
- Set the start with
AUTO_INCREMENT = n; TRUNCATE resets it. - Choose INT or BIGINT UNSIGNED with room to grow.