The Schema Decision That Cost Us Performance (And Could’ve Cost Us Money)
A practical deep dive into MySQL data types, covering INT, DECIMAL, CHAR, VARCHAR, DATETIME, and TIMESTAMP with real-world production insights.

I am a full-stack developer (currently working with cognizant) who is passionate about web development and creating digital products with innovative solutions.
I hold 6+ years of experience in development, understanding clients' unique needs, and delivering quality code. I've honed my coding skills over these years through both my professional exposure and self-study. Have worked with teams that have used agile development methodology.
I am someone who likes to keep acquiring new skills and capabilities. And when I am not a web developer, I enjoy reading, writing and running.
My interests include:
- Exploring new ideas
- Learning about future tech & products
- Finding user-friendly solutions to tech-related problems
- Sharing my knowledge with close acquaintances
We didn’t notice it at first.
Queries were “a little slow.”
Disk usage was “a bit high.”
Reports were “slightly off by a few cents.”
Nothing dramatic.
But when we scaled, everything magnified.
Indexes grew huge.
Backups took longer.
And a rounding issue in financial calculations almost made it to production billing.
The root cause?
Not bad logic.
Not bad architecture.
Bad data types.
That’s when I learned something most tutorials barely emphasize:
Data types are not syntax.
They are performance, cost, and reliability decisions.
In this guide, we are going to explore 4 key concepts to help you master MySQL data types in production systems:
Numeric types (INT, TINYINT, BIGINT)
DECIMAL vs FLOAT vs DOUBLE
CHAR vs VARCHAR
DATE, DATETIME, and TIMESTAMP
This isn’t about memorizing ranges.
This is about designing schemas that don’t break at scale.
1️⃣ Data Types: The Containers of Your System
At a surface level, data types define how MySQL stores data internally.
But in production, they determine:
Storage size
Performance
Accuracy
Index efficiency
The Container Analogy
Think of data types like container sizes.
If you store a teaspoon of salt in a 10-liter bucket, you waste space.
If you try to store 10 liters in a teaspoon, it overflows.
The right container = efficient system.
The wrong container = slow queries, wasted memory, and scaling pain.
🔢 Numeric Types — INT, TINYINT, BIGINT
What They Are
Whole number types.
| Type | Range (Signed) | Storage |
|---|---|---|
| TINYINT | -128 to 127 | 1 byte |
| INT | ~2 billion | 4 bytes |
| BIGINT | Very large | 8 bytes |
They look interchangeable.
They are not.
Real-World Scenario: Inventory System
You’re building an inventory system.
You need:
Product ID → INT
Stock quantity → INT
is_active flag → TINYINT
CREATE TABLE inventory (
id INT AUTO_INCREMENT PRIMARY KEY,
product_name VARCHAR(100),
quantity INT,
is_active TINYINT
);
Seems simple.
But here’s where things go wrong.
What Happens If You Use BIGINT Everywhere?
I’ve seen this a lot.
“Let’s just use BIGINT. Just in case.”
Sounds safe.
But:
BIGINT uses double the storage of INT.
Indexes become larger.
Memory usage increases.
Cache efficiency drops.
At scale, this matters.
If you have 50 million rows, doubling index size isn’t theoretical. It’s measurable.
Senior Dev Perspective
Use BIGINT when:
You expect distributed ID generation.
You’re designing for massive scale.
You genuinely risk INT overflow.
Otherwise?
Be intentional.
Small types → smaller indexes → faster joins.
And faster joins are everything.
💰 DECIMAL vs FLOAT vs DOUBLE — Where Money Gets Dangerous
This is where production mistakes become legal mistakes.
DECIMAL — Exact Precision
price DECIMAL(8,2)
8 total digits.
2 digits after decimal.
DECIMAL stores exact values.
No approximation.
Perfect for:
E-commerce pricing
Banking systems
Financial reporting
FLOAT and DOUBLE — Approximate Values
| Type | Precision |
|---|---|
| FLOAT | ~7 digits |
| DOUBLE | ~15 digits |
These are stored in binary floating-point format.
That means:
0.1 + 0.2 ≠ exactly 0.3
In code, this feels minor.
In finance, this is catastrophic.
What Happens If You Use FLOAT for Money?
You get rounding inconsistencies.
Now imagine:
Monthly invoices slightly off.
Aggregated totals misaligned.
Regulatory audits questioning discrepancies.
Congratulations. You’ve built a lawsuit generator.
When FLOAT/DOUBLE Are Correct
They’re perfect for:
Scientific calculations
Sensor readings
Analytics
Large statistical datasets
Where tiny rounding errors are acceptable.
Senior Dev Perspective
Rule of thumb:
Money → DECIMAL
Science → DOUBLE
Never guess
Precision errors don’t crash loudly.
They accumulate silently.
🔤 CHAR vs VARCHAR — The Hidden Memory Trap
This looks small.
It isn’t.
VARCHAR — Flexible & Efficient
Variable length
Stores actual size
Recommended for most text fields
name VARCHAR(100)
If the name is 5 characters, it stores 5 characters.
Efficient.
CHAR — Fixed Length
Always consumes full space
Faster for fixed-size data
Example:
country_code CHAR(2)
Perfect for:
Country codes (IN, US)
State abbreviations
Fixed-length hashes
What Happens If You Misuse CHAR?
Let’s say:
You store usernames in CHAR(100).
Every record consumes full 100 characters.
Multiply that by millions of rows.
Your disk usage explodes.
Backups grow.
Replication slows.
I/O increases.
All because of a schema decision.
Senior Dev Perspective
Use CHAR when:
Length is guaranteed fixed.
You want predictable storage.
Use VARCHAR otherwise.
Most real-world text fields? Use VARCHAR.
📅 Date vs DATETIME vs TIMESTAMP — Where Time Breaks Systems
Time is tricky.
Timezones are worse.
Example Table
CREATE TABLE people (
name VARCHAR(100),
birthdate DATE,
birthtime TIME,
birthdt DATETIME
);
What They Store
| Type | Stores |
|---|---|
| DATE | Date only |
| TIME | Time only |
| DATETIME | Full date & time |
| TIMESTAMP | Date & time |
But here’s the important part.
DATETIME vs TIMESTAMP — The Critical Differences
| Feature | DATETIME | TIMESTAMP |
|---|---|---|
| Storage | 8 bytes | 4 bytes |
| Timezone | No auto conversion | Timezone aware |
| Use case | Historical data | created_at / updated_at |
TIMESTAMP auto-converts based on timezone settings.
DATETIME does not.
What Happens If You Choose Wrong?
Let’s say:
You store audit logs in DATETIME.
Your app runs in different timezones.
Suddenly:
Logs don’t align.
Reports shift hours.
Analytics dashboards look wrong.
You lose trust in your own data.
Timezone bugs are among the most common production issues I’ve seen.
Auto Tracking Example
CREATE TABLE captions (
text VARCHAR(150),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE captions2 (
text VARCHAR(150),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
Perfect for metadata.
Senior Dev Perspective
Use:
DATE for pure dates.
DATETIME for historical records.
TIMESTAMP for metadata and audit fields.
Always standardize to UTC.
Time is hard. Don’t make it harder.
📆 Date & Time Functions — Reporting Power
Examples:
SELECT DAY(birthdate), MONTHNAME(birthdate), YEAR(birthdate) FROM people;
SELECT DATE_FORMAT(birthdate, '%a %b %D') FROM people;
Used for:
Monthly reports
Sales breakdowns
Business intelligence dashboards
Correct data types make these queries efficient.
Wrong ones make them painful.
3️⃣ Common Production Pitfalls
Let’s be honest.
We’ve all done at least one of these:
Used FLOAT for money
Used BIGINT everywhere
Used CHAR for user input
Ignored timezone handling
Stored dates as VARCHAR
Storing dates as VARCHAR is the ultimate “I’ll fix it later.”
You won’t.
You’ll migrate it painfully two years from now.
🛠 DevOps / SRE Angle
Data types affect:
Backup size
Replication speed
Query performance
Disk I/O
Index efficiency
Schema mistakes multiply at scale.
Fixing them later requires:
Downtime
Migrations
Risk
Choosing correctly early is cheaper.
🧠 Mental Model to Lock In
Data types are not academic details.
They are:
Performance decisions
Cost decisions
Reliability decisions
Legal decisions (finance)
Choosing correct types separates:
Tutorial developers
from
Production engineers
The Bridge Back
Remember the “small” performance issue that started this story?
We optimized indexes.
Replaced unnecessary BIGINT.
Switched FLOAT to DECIMAL.
Standardized timestamps to UTC.
Query times dropped.
Storage shrank.
Reports aligned.
The code didn’t change.
The schema did.
And that’s when it clicks:
Great backend engineers don’t just write logic.
They design storage with intention.
Because the database never forgets your mistakes.




