SQL AUTO_INCREMENT
AUTO_INCREMENT makes MySQL generate the next number for a column every time you insert a row, so you never have to work out a new id yourself. It is the standard way to create surrogate primary keys. This lesson shows how it counts, how to get the id you just created, and why the numbers sometimes skip.
- Create an AUTO_INCREMENT primary key
- Get the new id with
LAST_INSERT_ID() - Set or reset the starting value
- Understand gaps in the sequence
Define an AUTO_INCREMENT column
CREATE TABLE tickets ( ticket_id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(60) NOT NULL);INSERT INTO tickets (title) VALUES ('Login fails on Safari');INSERT INTO tickets (title) VALUES ('Report export is slow'), ('Typo on pricing page');SELECT * FROM tickets;| ticket_id | title |
|---|---|
| 1 | Login fails on Safari |
| 2 | Report export is slow |
| 3 | Typo on pricing page |
ticket_idis not given in the inserts; MySQL fills in 1, 2 and 3.- The column must be a key (usually the primary key), and a table can have only one AUTO_INCREMENT column.
- Inserting
NULLor0into the column also generates the next value.
LAST_INSERT_ID()
After an insert, LAST_INSERT_ID() returns the id that was generated, which you need to insert child rows:
CREATE TABLE tickets (ticket_id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(60) NOT NULL);CREATE TABLE ticket_comments (comment_id INT AUTO_INCREMENT PRIMARY KEY, ticket_id INT NOT NULL, body VARCHAR(100));INSERT INTO tickets (title) VALUES ('Checkout button missing');INSERT INTO ticket_comments (ticket_id, body) VALUES (LAST_INSERT_ID(), 'Reproduced on Chrome');SELECT t.ticket_id, t.title, c.body FROM tickets t JOIN ticket_comments c ON c.ticket_id = t.ticket_id;| ticket_id | title | body |
|---|---|---|
| 1 | Checkout button missing | Reproduced on Chrome |
- The ticket insert generates
ticket_id = 1. LAST_INSERT_ID()returns 1, so the comment is attached to the right ticket.- The value is per connection: other users inserting at the same time cannot change what your session sees.
- For a multi-row insert it returns the id of the first inserted row.
Choose the starting value
CREATE TABLE invoices ( invoice_no INT AUTO_INCREMENT PRIMARY KEY, amount INT NOT NULL) AUTO_INCREMENT = 1001;INSERT INTO invoices (amount) VALUES (4999), (1250);SELECT * FROM invoices;| invoice_no | amount |
|---|---|
| 1001 | 4999 |
| 1002 | 1250 |
Change it later with ALTER TABLE invoices AUTO_INCREMENT = 5000; (it cannot be set below the current maximum).
Why there are gaps
Auto-increment values are never reused. Deleted rows, rolled-back inserts and failed inserts all leave gaps:
CREATE TABLE tickets (ticket_id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(60) NOT NULL UNIQUE);INSERT INTO tickets (title) VALUES ('A'), ('B'), ('C');DELETE FROM tickets WHERE ticket_id = 3;INSERT INTO tickets (title) VALUES ('D');INSERT IGNORE INTO tickets (title) VALUES ('A');INSERT INTO tickets (title) VALUES ('E');SELECT * FROM tickets;| ticket_id | title |
|---|---|
| 1 | A |
| 2 | B |
| 4 | D |
| 6 | E |
- A, B, C get 1, 2, 3. Deleting 3 does not free the number: D gets 4.
INSERT IGNOREof the duplicate'A'fails the unique check but has already used up number 5.- E therefore gets 6.
Treat auto-increment ids as unique labels, not as a count or an order of events. If you need gap-free invoice numbers for legal reasons, generate them separately inside a transaction.
TRUNCATE resets the counter
DELETE FROM t keeps the counter; TRUNCATE TABLE t resets it to 1 (see DELETE & TRUNCATE).
In other databases
| Database | Syntax |
|---|---|
| MySQL | id INT AUTO_INCREMENT PRIMARY KEY |
| PostgreSQL | id INT GENERATED ALWAYS AS IDENTITY (or SERIAL) |
| SQL Server | id INT IDENTITY(1,1) PRIMARY KEY |
| SQLite | id INTEGER PRIMARY KEY (auto-assigned) |
| Oracle 12c+ | id NUMBER GENERATED ALWAYS AS IDENTITY |
Quick quiz
1. How do you get the id generated by your last insert in MySQL?
LAST_INSERT_ID() is per connection and safe with concurrent users.
2. A row with id 7 is deleted. Is 7 reused?
Numbers are never reused automatically.
3. Why is SELECT MAX(id) a bad way to find your new id?
Use LAST_INSERT_ID(), which is per connection.
AUTO_INCREMENT FAQ
What is AUTO_INCREMENT in MySQL?
A column attribute that makes MySQL assign the next integer automatically when a row is inserted without a value for that column.
How do I reset AUTO_INCREMENT in MySQL?
TRUNCATE TABLE t empties the table and resets it, or ALTER TABLE t AUTO_INCREMENT = n sets the next value (at least the current maximum + 1).
Why does my AUTO_INCREMENT skip numbers?
Deletes, rolled-back transactions, failed inserts and INSERT IGNORE / ON DUPLICATE KEY all consume values without leaving rows.