SQL Data Types
Every column has a data type that decides what values it accepts, how much space it takes and how it sorts and compares. Choosing well prevents bugs such as rounding errors in money or names cut short. This lesson shows the MySQL types you will actually use, and what MySQL 8.4 does when a value does not fit.
- Pick the right numeric type, and why money needs
DECIMAL CHARvsVARCHARvsTEXT- Store dates and times with
DATE,DATETIMEandTIMESTAMP - What happens when a value is too big or too long
Numeric types
| Type | Stores | Range / precision | Typical use |
|---|---|---|---|
TINYINT | 1 byte | -128 to 127 | flags, small counts |
SMALLINT | 2 bytes | -32,768 to 32,767 | quantities |
INT | 4 bytes | about ±2.1 billion | ids, counts, salaries in whole rupees |
BIGINT | 8 bytes | about ±9.2 × 10^18 | very large ids, bytes |
DECIMAL(p,s) | exact | p digits, s after the point | money, rates |
FLOAT / DOUBLE | approximate | 7 / 15 significant digits | measurements, science |
Add UNSIGNED to an integer type to allow only zero and positive numbers and double the upper limit.
DECIMAL vs FLOAT: why money must be exact
FLOAT and DOUBLE store numbers in binary, so most decimal fractions are only approximated. DECIMAL stores them exactly:
SELECT 0.1e0 + 0.2e0 AS double_sum, 0.1 + 0.2 AS decimal_sum;| double_sum | decimal_sum |
|---|---|
| 0.30000000000000004 | 0.3 |
0.1e0is written in scientific notation, so MySQL treats it as aDOUBLE. The binary sum is 0.30000000000000004.0.1without an exponent is an exactDECIMALliteral, so the sum is exactly 0.3.- A tiny error like this, repeated over thousands of invoices, is why prices and balances use
DECIMAL(10,2).
Values that do not fit
MySQL 8.4 runs in strict mode by default: a value that does not fit is an error, not a silent change.
CREATE TABLE stock_check (qty TINYINT);INSERT INTO stock_check VALUES (300);ERROR 1264 (22003): Out of range value for column 'qty' at row 1
TINYINTholds -128 to 127.- 300 is out of range, so MySQL rejects the row with error 1264 instead of storing 127.
String types
| Type | Stores | Notes |
|---|---|---|
CHAR(n) | exactly n characters | padded with spaces; good for fixed codes like 'IN' |
VARCHAR(n) | up to n characters | the usual choice for names, emails, titles |
TEXT | up to 64 KB | long descriptions, comments |
ENUM('a','b') | one value from a list | small fixed sets such as statuses |
Text longer than the column allows is rejected:
CREATE TABLE users (username VARCHAR(8));INSERT INTO users VALUES ('sqatools_admin');ERROR 1406 (22001): Data too long for column 'username' at row 1
ENUM accepts only the listed values:
CREATE TABLE tickets (id INT, status ENUM('open', 'in_progress', 'closed'));INSERT INTO tickets VALUES (1, 'open');INSERT INTO tickets VALUES (2, 'pending');ERROR 1265 (01000): Data truncated for column 'status' at row 1
- The table is created and the first insert succeeds, because
'open'is in the list. 'pending'is not one of the allowed values, so the second insert fails.
Many teams prefer a VARCHAR column plus a CHECK constraint or a lookup table instead of ENUM, because adding a value to an ENUM needs an ALTER TABLE.
Date and time types
| Type | Format | Example |
|---|---|---|
DATE | YYYY-MM-DD | '2026-01-31' |
TIME | HH:MM:SS | '09:30:00' |
DATETIME | YYYY-MM-DD HH:MM:SS | '2026-01-31 09:30:00' |
TIMESTAMP | like DATETIME, stored in UTC | converted to the session time zone |
YEAR | YYYY | 2026 |
Date arithmetic understands the calendar:
SELECT DATE '2026-01-31' + INTERVAL 1 MONTH AS next_month, DATE '2024-02-28' + INTERVAL 1 DAY AS leap_day, DATEDIFF('2026-12-25', '2026-01-01') AS days_between;| next_month | leap_day | days_between |
|---|---|---|
| 2026-02-28 | 2024-02-29 | 358 |
- One month after 31 January 2026 is 28 February, because February has no 31st.
- 2024 is a leap year, so the day after 28 February is the 29th.
DATEDIFF()counts the days between two dates: 358.
Learn more in Date & Time Functions.
BOOLEAN
MySQL has no separate boolean type: BOOLEAN is a synonym for TINYINT(1), TRUE is 1 and FALSE is 0.
CREATE TABLE features (name VARCHAR(20), enabled BOOLEAN);INSERT INTO features VALUES ('dark_mode', TRUE), ('beta_api', FALSE);SELECT name, enabled FROM features;| name | enabled |
|---|---|
| dark_mode | 1 |
| beta_api | 0 |
JSON
A JSON column stores validated JSON documents, and you can read values from them in queries:
CREATE TABLE settings (user_id INT, prefs JSON);INSERT INTO settings VALUES (1, '{"theme": "dark", "font_size": 14}');SELECT user_id, prefs->>'$.theme' AS theme, prefs->>'$.font_size' AS font_sizeFROM settings;| user_id | theme | font_size |
|---|---|---|
| 1 | dark | 14 |
- MySQL checks that the text is valid JSON before storing it.
->>extracts a value by path and returns it as plain text:$.themeis thethemekey.
How to choose a data type
- Ids and counts:
INT(orBIGINTfor very large tables). - Money:
DECIMAL(10,2), neverFLOAT. - Names, emails, codes:
VARCHARwith a sensible limit. - Long text:
TEXT. - Dates:
DATE; date and time:DATETIME; “when was this row changed”:TIMESTAMP. - Yes/no:
BOOLEAN. - Choose the smallest type that safely fits the data: smaller rows mean faster queries and smaller indexes.
Quick quiz
1. Which type should store product prices?
DECIMAL is exact, so sums and comparisons of money are correct.
2. What is the difference between CHAR(10) and VARCHAR(10)?
CHAR pads to the full length; VARCHAR stores the actual length.
3. In strict mode, what happens when you insert 300 into a TINYINT column?
Strict mode turns data loss into an error.
4. What does BOOLEAN become in MySQL?
TRUE is stored as 1 and FALSE as 0.
Data types FAQ
What are the main data types in SQL?
Numeric (INT, DECIMAL, FLOAT), string (CHAR, VARCHAR, TEXT), date and time (DATE, DATETIME, TIMESTAMP) and others such as BOOLEAN and JSON.
What is the difference between DATETIME and TIMESTAMP in MySQL?
DATETIME stores the date and time exactly as given. TIMESTAMP converts the value to UTC for storage and back to the session time zone when read, and its range ends in 2038.
Should I use VARCHAR(255) for every text column?
No. Choose a limit that matches the data, such as VARCHAR(100) for an email. Realistic limits act as validation and keep indexes smaller.
Why is FLOAT bad for money?
FLOAT and DOUBLE store binary approximations, so values like 0.1 cannot be stored exactly and totals drift. DECIMAL stores exact decimal values.