The Complete Guide to SQL Data Types: Categories, Selection, and Best Practices

A practical guide to SQL data type categories, selection principles, and common pitfalls for efficient database design.
This article covers the core knowledge of SQL data types, including numeric (integer, float, DECIMAL), string (CHAR, VARCHAR, TEXT), and datetime (DATETIME, TIMESTAMP) types — their characteristics, use cases, and trade-offs in storage, precision, and performance. It introduces the minimalism principle for type selection, highlights the importance of constraints like NOT NULL, CHECK, and UNIQUE for data integrity, and warns about cross-database compatibility differences. Common pitfalls such as overusing VARCHAR(255), storing booleans as strings, and mishandling enums are also addressed.
What Are SQL Data Types
SQL data types are the foundation of database design. They define the kind and range of values that can be stored in each column of a table. Choosing the right data type doesn't just affect storage efficiency — it directly impacts query performance, data integrity, and application reliability.
At their core, data types serve as a constraint mechanism, telling the database management system (DBMS) how to interpret and handle data. For example, defining an age field as an integer (INT) rather than a string (VARCHAR) prevents users from entering invalid values like "twenty years old", while also enabling proper numeric comparisons and calculations.
Major SQL Data Type Categories
Numeric Types: Integers and Floating-Point Numbers
Numeric types are used to store numbers, and they fall into two broad categories: integer types and floating-point types.
Integer types include TINYINT, SMALLINT, INT, and BIGINT, which differ in their storage range and space requirements:
| Type | Storage | Range (Unsigned) |
|---|---|---|
| TINYINT | 1 byte | 0 ~ 255 |
| SMALLINT | 2 bytes | 0 ~ 65,535 |
| INT | 4 bytes | 0 ~ ~4.29 billion |
| BIGINT | 8 bytes | 0 ~ ~1.8×10¹⁹ |
Floating-point types like FLOAT and DOUBLE store decimal numbers, but they suffer from precision loss. For scenarios requiring exact calculations — such as financial data — use DECIMAL or NUMERIC instead. These types let you specify precision and scale, for example DECIMAL(10,2) means 10 total digits with 2 decimal places.
DECIMAL and NUMERIC are semantically equivalent in standard SQL. Both store numbers exactly in decimal form, avoiding the rounding errors caused by the binary floating-point representation used by FLOAT/DOUBLE. In the format DECIMAL(p, s), p (precision) is the total number of significant digits and s (scale) is the number of decimal places. For example, DECIMAL(10,2) can store a maximum value of 99999999.99. The trade-off is that exact storage is slightly slower to compute than floating-point types, and storage size grows with precision. In high-frequency aggregation scenarios where strict precision isn't required, DOUBLE may still be a reasonable choice. Also note that starting from MySQL 8.0, specifying precision for FLOAT and DOUBLE (e.g., FLOAT(7,2)) has been deprecated — it's recommended to use DECIMAL for all business scenarios that require precise decimal handling.
String Types: Choosing Between CHAR and VARCHAR
String types are among the most commonly used. CHAR is a fixed-length string type, while VARCHAR is variable-length.
- CHAR(50): Always occupies 50 characters of space, even if only 5 characters are stored
- VARCHAR(50): Dynamically allocates space based on actual content, offering more flexibility
The choice between CHAR and VARCHAR depends on your data characteristics:
- Fixed-length data (e.g., country codes, ID numbers) →
CHARoffers better performance - Variable-length data (e.g., user comments, article content) →
VARCHARsaves more space
TEXT and BLOB types are used for large text or binary data, but they cannot have default values and are not suitable for indexing — keep these limitations in mind when using them.
Date and Time Types: DATETIME vs. TIMESTAMP
Date and time types include DATE, TIME, DATETIME, and TIMESTAMP:
- DATE: Stores only the date (year, month, day)
- TIME: Stores only the time (hour, minute, second)
- DATETIME: Stores a complete date and time value
- TIMESTAMP: Stores date and time with timezone conversion
The key differences between TIMESTAMP and DATETIME lie in timezone handling and storage range. TIMESTAMP automatically converts values based on the server's timezone, making it suitable for cross-timezone scenarios (such as user activity logs). DATETIME stores the input value as-is, without any timezone conversion. In addition, TIMESTAMP supports the range from 1970 to 2038, while DATETIME can store dates from 1000 to 9999.
Under the hood, TIMESTAMP stores values in UTC (Coordinated Universal Time) — converting from the current session timezone to UTC on write, and back to the session timezone on read. DATETIME, by contrast, stores the literal value exactly as entered, with no timezone conversion. This means that if the database server is migrated to a different timezone or the time_zone setting is changed, TIMESTAMP column values will change accordingly, while DATETIME columns remain unaffected. The 2038 upper limit for TIMESTAMP stems from its use of a 32-bit signed integer to store the Unix timestamp (seconds since 1970-01-01 00:00:00 UTC), with a maximum value of approximately 2^31-1 seconds, corresponding to January 19, 2038. MySQL 8.0.28+ has extended some storage to mitigate this issue, but for long-lifecycle business data (such as contracts or insurance records), it's still advisable to prefer DATETIME.
Best Practices for Choosing SQL Data Types
Follow the Principle of Minimalism
Choose the smallest data type that satisfies your requirements. For example, storing user age as TINYINT instead of INT saves 75% of storage space. When a table has millions of rows, this kind of optimization leads to significant performance improvements and cost reductions.
Prioritize Data Integrity Constraints
Use constraints thoughtfully to ensure data quality:
- NOT NULL: Prevents null values from being inserted
- CHECK: Validates data ranges (e.g., age must be between 0 and 150)
- UNIQUE: Ensures field values are unique
These constraints provide an additional layer of protection on top of data types, and are an essential part of building a reliable database.
Be Aware of Cross-Database Compatibility
Data type implementations vary across database systems. MySQL's AUTO_INCREMENT corresponds to SERIAL in PostgreSQL and IDENTITY in SQL Server. If your application needs to support multiple databases, account for these differences at the design stage, or use an ORM framework to abstract the underlying details.
Index and Query Performance Optimization
The data type of indexed columns has a major impact on query performance. Integer type index lookups are far faster than string type lookups, which is why primary keys typically use INT or BIGINT rather than UUID strings. For foreign key fields that are frequently used in JOIN operations, always ensure both sides use the same data type and length — mismatches cause implicit type conversion, which invalidates indexes and can severely slow down queries.
Implicit type conversion is one of the most common causes of index invalidation. When data types don't match on either side of a WHERE clause or JOIN condition, the database engine must convert each row at runtime, making it impossible to use the index for fast lookups and forcing a full table scan instead — degrading query complexity from O(log n) to O(n). A classic example: if a phone number field is defined as VARCHAR but the query passes in a number like WHERE phone = 13800138000, MySQL will convert the entire column to a numeric value before comparing, completely invalidating the index. While UUIDs as primary keys offer advantages like global uniqueness and no sequence exposure, their string form (36 bytes) takes up significantly more space than BIGINT (8 bytes), and random inserts cause frequent B+ tree page splits that hurt write performance. A middle-ground approach is to store UUIDs in binary form using BINARY(16), or to use ordered UUIDs (such as UUIDv7) to improve data locality.
Common Pitfalls and Solutions
Avoid Overusing VARCHAR(255)
Many developers default to VARCHAR(255) to "stay flexible," but this wastes space and can cause performance issues. The correct approach is to define lengths precisely based on actual business requirements: 30 to 50 characters is usually sufficient for usernames, and 100 characters will cover virtually all email addresses.
Use the Correct Boolean Type
Another common mistake is storing boolean values as strings (e.g., 'Y'/'N' or 'true'/'false'). Modern databases support BOOLEAN or BIT types, which are not only semantically clearer but also more space-efficient and easier to query.
Handle Enumerated Data Correctly
For enumerated data (such as order status or user roles), use the ENUM type or associate values with a lookup table via foreign keys — don't just store raw strings. This ensures data consistency, prevents typos, and makes future changes easier.
MySQL's ENUM type internally maps enumeration strings to integers, making storage compact and comparisons efficient. However, its major drawback is that modifying the list of enum values requires executing ALTER TABLE, which is a costly DDL operation on large tables and can cause prolonged table locks. In contrast, using a separate lookup table with a foreign key relationship allows new enum values to be added with a simple INSERT statement — offering more flexibility and better adherence to database normalization principles. PostgreSQL provides native CREATE TYPE ... AS ENUM syntax, and also supports ALTER TYPE ... ADD VALUE to dynamically append enum values (though existing values cannot be deleted or modified). When enum values are stable and limited in number during the early stages of a project, ENUM is a reasonable choice. If you anticipate frequent changes to the enum values, prefer the lookup table approach.
Conclusion
Choosing the right SQL data types is a critical decision in database design — one that affects system performance, maintainability, and scalability. Following the principle of minimalism, prioritizing data integrity, considering cross-platform compatibility, and making informed trade-offs based on actual business requirements are all foundational to designing a high-quality database architecture. Investing time upfront to make the right data type decisions can save you from costly architectural refactoring down the road.
Related articles

Astra Hands-On: Full Workflow Automation from AI Concept Art to 3D Modeling
A deep dive into how AI tool Astra auto-generates reference images and builds 3D models in Blender from text descriptions — covering its pipeline, real-world performance, limitations, and industry impact.

Zed Editor v1.17.2-pre Released: Git Panel Crash Fix Explained
Zed editor releases v1.17.2-pre, fixing a crash bug in the Git panel's tree view collapsed sections mode. Learn about Zed's latest updates and core advantages.

Krea2 Mixed-Media Rendering Workflow: How to Blend Anime Characters with Realistic Backgrounds
A full breakdown of the Krea2 Turbo mixed-media workflow: anime characters on realistic backgrounds, with parameter settings, LoRA tips, structured prompt design, and MooshieUI guidance.