MySQL Data Types and Constraints
Three inserts that fail, each for a different reason and with a different error number. A constraint you never see fire is a constraint you do not know you have - so this guide fires all of them.
Schemas and Data Guide 7 of 45 Beginner
- OSUbuntu 26.04 LTS (resolute)
- MySQL8.4.10-0ubuntu0.26.04.1
- EngineInnoDB (default)
- TimeAbout 15 min
- Reviewed27 August 2026
Written against the versions above. Strict SQL mode is the default from MySQL 5.7 onward. On an older server or one with `sql_mode` relaxed, the first of these truncates silently instead of failing, which is the behaviour strict mode was introduced to end.
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| db-a01 | 192.168.0.81 | Ubuntu 26.04 LTS | Primary / Source / Replica Set Member 1 | 2 Core | 4 GB | 50 GB |
Before you start
- The
customerstable from the previous guide, with its three rows.
-
Start from data that is already there
Three customers, inserted in one statement. Multi-row
INSERTis one round trip and one transaction rather than three, which matters more than it looks at scale.Read the result table.
idwas filled byAUTO_INCREMENT,created_atby the server clock, and Grace'screditshows0.00- the column default, not the zero that was passed. Both mechanisms produced a value nobody typed per row.bash Example session sudo mysql appdb -e "INSERT INTO customers (name, email, credit) VALUES ('Ada Lovelace','ada@example.com',120.50),('Grace Hopper','grace@example.com',0),('Katsuko Saruhashi','katsuko@example.com',75.25)"sudo mysql --table appdb -e "SELECT * FROM customers"+----+-------------------+---------------------+--------+---------------------+| id | name | email | credit | created_at |+----+-------------------+---------------------+--------+---------------------+| 1 | Ada Lovelace | ada@example.com | 120.50 | 2026-08-27 12:47:19 || 2 | Grace Hopper | grace@example.com | 0.00 | 2026-08-27 12:47:19 || 3 | Katsuko Saruhashi | katsuko@example.com | 75.25 | 2026-08-27 12:47:19 |+----+-------------------+---------------------+--------+---------------------+Expected resultThree rows with generated ids and timestamps.
Success conditionYou have rows to violate constraints against.
-
Exceed the range of a column
DECIMAL(10,2)means ten digits total, two after the point - so the largest value it holds is 99999999.99. The insert tries 99999999999.99.ERROR 1264and exit 1. Nothing was written and nothing was truncated. This is strict SQL mode, and it is the default: without it MySQL would have stored the maximum value, returned a warning nobody reads, and left you with a wrong number that looks right.The error names the column and the row, which is what makes a multi-row insert debuggable.
bash Example session sudo mysql appdb -e "INSERT INTO customers (name, email, credit) VALUES ('Too Long', 'dup@example.com', 99999999999.99)" ; echo "exit=$?"ERROR 1264 (22003) at line 1: Out of range value for column 'credit' at row 1exit=1Expected result
ERROR 1264 (22003): Out of range value for column 'credit' at row 1andexit=1.Success conditionYou have seen strict mode refuse rather than silently truncate.
-
Insert a duplicate into a unique column
The email column was declared
UNIQUE, and the previous guide showed MySQL turning that into a named index.ERROR 1062, and read what it names: the value AND the key,customers.email. On a table with several unique constraints that is the difference between a two-second fix and a guessing game.This is the error behind most failed application signups, and it is a feature - the database refusing to let two accounts share an identity.
bash Example session sudo mysql appdb -e "INSERT INTO customers (name, email) VALUES ('Ada Again','ada@example.com')" ; echo "exit=$?"ERROR 1062 (23000) at line 1: Duplicate entry 'ada@example.com' for key 'customers.email'exit=1Expected result
ERROR 1062 (23000): Duplicate entry 'ada@example.com' for key 'customers.email'andexit=1.Success conditionYou can read which constraint rejected a row from the error alone.
-
Omit something that has no default
An insert that supplies email but not name.
ERROR 1364, and the wording is precise: not "name is required" but *"Field 'name' doesn't have a default value"*. That is the real rule. ANOT NULLcolumn with a default is fine to omit; one without has nothing to fall back on, so the statement fails.Compare with
credit, which is alsoNOT NULLand was omitted happily in the first step - because it hasDEFAULT 0.00.bash Example session sudo mysql appdb -e "INSERT INTO customers (email) VALUES ('noname@example.com')" ; echo "exit=$?"ERROR 1364 (HY000) at line 1: Field 'name' doesn't have a default valueexit=1Expected result
ERROR 1364 (HY000): Field 'name' doesn't have a default valueandexit=1.Success conditionYou know the difference between NOT NULL and NOT NULL with a default.
-
Store four-byte Unicode and count it two ways
A name with an accent and an emoji. It inserts without complaint, which is the point - the column is utf8mb4 because the database was created that way.
The two length functions are the lesson.
CHAR_LENGTHis 15 andLENGTHis 19. Fifteen characters, nineteen bytes: the accented character costs two and the emoji four.This is why
VARCHAR(80)means 80 characters rather than 80 bytes, and why byte-based length checks in application code reject names that are perfectly valid.bash Example session sudo mysql appdb -e "INSERT INTO customers (name, email) VALUES ('Zoë Washburne 🚀','zoe@example.com')"sudo mysql --table appdb -e "SELECT id, name, CHAR_LENGTH(name) AS chars, LENGTH(name) AS bytes FROM customers WHERE id > 3"+----+---------------------+-------+-------+| id | name | chars | bytes |+----+---------------------+-------+-------+| 5 | Zoë Washburne 🚀 | 15 | 19 |+----+---------------------+-------+-------+Expected resultThe insert succeeds, then one row with
chars15 andbytes19.Success conditionYou can tell characters from bytes and know which one the column limits.
Troubleshooting
ERROR 1264on a value that looks within range.Why:
DECIMAL(p,s)counts total digits, not digits before the point.DECIMAL(10,2)allows eight digits before it, not ten.Fix:Widen the precision deliberately -
DECIMAL(12,2)- rather than switching to a float, which trades a hard error for a quiet inaccuracy.The same insert succeeds on one server and fails on another.
Why:
sql_modediffers. Strict mode is default but is often relaxed by an inherited config.Fix:
SELECT @@sql_modeon both. Prefer to fix the data rather than relax the mode.ERROR 1062on a row your application checked for first.Why: A race: two requests both checked, both found nothing, both inserted.
Fix:Let the constraint be the check. Catch 1062 and handle it - the database does this atomically and your application cannot.