Himanshu SharmaSoftware engineer
Let’s talk

MySQL · Lesson 30 of 50

Data types

Choose the right MySQL data type for numbers, text, dates, flags and JSON, and understand ranges, DECIMAL precision, VARCHAR versus CHAR and strict mode errors.

16 minBuilding databasesUpdated 22 October 2026

Awaiting technical review. This lesson is readable but not yet listed in search engines.

By the end of this lessonPick suitable data types for columns and predict what MySQL does with values that are too big or too long.

Why types matter

Every column has a data type. The type decides which values are allowed, how much disk the column uses, how it sorts and compares, and how fast queries run. A bad choice causes subtle bugs, such as prices that drift by a cent or dates that sort alphabetically. A good choice also documents your intent.

Integers

Type Bytes Signed range Unsigned range
TINYINT 1 -128 to 127 0 to 255
SMALLINT 2 -32,768 to 32,767 0 to 65,535
MEDIUMINT 3 about ±8.4 million 0 to 16.7 million
INT 4 about ±2.1 billion 0 to 4.29 billion
BIGINT 8 about ±9.2 quintillion 0 to 1.8e19

Add UNSIGNED when a value can never be negative, such as a quantity or an age. It doubles the positive range. Use INT for most ids, and BIGINT for tables that may grow past a few billion rows. BOOLEAN is an alias for TINYINT(1): 0 is false, 1 is true.

Let us see what strict mode does with an out-of-range value:

sql
CREATE DATABASE IF NOT EXISTS practice;
USE practice;
DROP TABLE IF EXISTS ages;
CREATE TABLE ages (person VARCHAR(20), age TINYINT UNSIGNED, is_member BOOLEAN DEFAULT FALSE);
INSERT INTO ages (person, age) VALUES ('Mia', 34);
SELECT * FROM ages;
result
+--------+------+-----------+
| person | age  | is_member |
+--------+------+-----------+
| Mia    |   34 |         0 |
+--------+------+-----------+
sql
INSERT INTO ages (person, age) VALUES ('Old Tree', 300);
result
ERROR 1264 (22003): Out of range value for column 'age' at row 1

The default SQL mode of MySQL 8 is strict, so the bad value is rejected with an error. In a non-strict mode the value would be silently clamped to 255, which is the kind of quiet corruption you want to avoid. Keep strict mode on.

Exact and approximate numbers

  • DECIMAL(p, s) (also NUMERIC) stores exact decimals. DECIMAL(8,2) means 8 digits in total, 2 after the point, so the largest is 999999.99.
  • FLOAT (4 bytes) and DOUBLE (8 bytes) store approximations. They are right for measurements and scientific values, wrong for money.
sql
DROP TABLE IF EXISTS prices;
CREATE TABLE prices (item VARCHAR(20), price DECIMAL(6,2));
INSERT INTO prices VALUES ('tea', 3.456);
SELECT * FROM prices;
result
+------+-------+
| item | price |
+------+-------+
| tea  |  3.46 |
+------+-------+

The value was rounded to two decimals. Inserting a number that needs more digits before the point than p - s allows (for example 99999.99 into DECIMAL(6,2)) gives an out-of-range error.

Text

Type Use
CHAR(n) fixed length, padded with spaces. Good for codes such as 'IN' or a 32-character hash
VARCHAR(n) variable length up to n characters (up to 65,535 bytes in total per row). The everyday choice for names, emails and titles
TEXT, MEDIUMTEXT, LONGTEXT long content: articles, comments, descriptions
ENUM('a','b') one value from a fixed list, stored compactly

Choose a realistic VARCHAR length. A limit of 100 for a name is plenty; 255 is a common upper bound for emails and URLs. Over-long values are rejected in strict mode:

sql
DROP TABLE IF EXISTS codes;
CREATE TABLE codes (country_code CHAR(2), label VARCHAR(5), size ENUM('S','M','L'));
INSERT INTO codes VALUES ('IN', 'India', 'M');
SELECT * FROM codes;
result
+--------------+-------+------+
| country_code | label | size |
+--------------+-------+------+
| IN           | India | M    |
+--------------+-------+------+
sql
INSERT INTO codes VALUES ('IN', 'Hindustan', 'M');
result
ERROR 1406 (22001): Data too long for column 'label' at row 1
sql
INSERT INTO codes VALUES ('IN', 'India', 'XL');
result
ERROR 1265 (01000): Data truncated for column 'size' at row 1

The first error is "Data too long for column"; the second is "Data truncated" because 'XL' is not one of the allowed ENUM values. ENUM is convenient for a short, stable list such as an order status. If the list may change often, a separate lookup table is more flexible, as you will see in the design lesson.

Dates and times

Type Example Notes
DATE 2024-05-02 a day
DATETIME 2024-05-02 13:45:00 day and time, stored as written
TIMESTAMP 2024-05-02 13:45:00 stored in UTC, converted to the session time zone; range 1970 to 2038
TIME 13:45:00 a time or a duration
YEAR 2024 a four-digit year

Never store dates as text. Real date types validate, sort and calculate correctly:

sql
SELECT CAST('2024-02-30' AS DATE) AS impossible_date;
result
+-----------------+
| impossible_date |
+-----------------+
| NULL            |
+-----------------+
sql
SELECT DATE('2024-02-29') + INTERVAL 1 YEAR AS next_year;
result
+------------+
| next_year  |
+------------+
| 2025-02-28 |
+------------+

The first query returns NULL, because 30 February does not exist. The second shows how MySQL steps from a leap day to the end of February.

JSON and binary

JSON stores a document and validates that it is well-formed; a later lesson covers it. BLOB types store raw bytes such as images, but most applications keep files on disk or in object storage and store only the path in the database.

Choosing quickly

Data Good type
Row id INT UNSIGNED AUTO_INCREMENT (or BIGINT)
Money DECIMAL(10,2)
Name, title, email VARCHAR(100) to VARCHAR(255)
Long text TEXT
Yes/no flag BOOLEAN
Birthday DATE
Event time DATETIME or TIMESTAMP
Fixed short code CHAR(n)

Common mistakes

  • Using FLOAT for money.
  • Using VARCHAR(255) for everything without thinking about meaning or size.
  • Storing dates or numbers as text.
  • Using INT(11) to mean "11 digits". The number in brackets was only a display hint and is deprecated; INT always holds the same range.
  • Turning off strict mode to make errors go away.

Exercise

Design a table products_demo for a product with an id, a name (up to 80 characters), a price in rupees with paise, an in_stock flag, a weight_kg measurement (approximate is fine) and a created_at moment. Insert one row, then try inserting a price of 123456789.00 and read the error.

Show one possible answer
sql
DROP TABLE IF EXISTS products_demo;
CREATE TABLE products_demo (
  id         INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
  name       VARCHAR(80) NOT NULL,
  price      DECIMAL(10,2) NOT NULL,
  in_stock   BOOLEAN NOT NULL DEFAULT TRUE,
  weight_kg  FLOAT,
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO products_demo (name, price, weight_kg) VALUES ('Notebook', 49.50, 0.35);
SELECT id, name, price, in_stock FROM products_demo;
result
+----+----------+-------+----------+
| id | name     | price | in_stock |
+----+----------+-------+----------+
|  1 | Notebook | 49.50 |        1 |
+----+----------+-------+----------+
sql
INSERT INTO products_demo (name, price) VALUES ('Too expensive', 123456789.00);
result
ERROR 1264 (22003): Out of range value for column 'price' at row 1

The second insert fails with "Out of range value for column 'price'", because DECIMAL(10,2) has room for only 8 digits before the point.

Key points

  • Integers come in TINYINT, SMALLINT, MEDIUMINT, INT and BIGINT sizes, optionally UNSIGNED. Use the smallest that comfortably fits.
  • Use DECIMAL(p, s) for money and exact numbers; FLOAT and DOUBLE are approximate.
  • VARCHAR(n) stores variable-length text up to n characters, CHAR(n) is fixed length, and TEXT is for long content.
  • Use DATE, DATETIME or TIMESTAMP for time values, never text, and BOOLEAN (really TINYINT(1)) for flags.

Check yourself

1. Which type should store a price like 1299.99?
2. What happens in strict mode when you insert 300 into a TINYINT UNSIGNED column?
3. Which is best for storing a country code that is always exactly two letters?
4. Which type stores a moment in time that is converted between time zones?