Best data type to store money values in MySQL
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Use DECIMAL(19, 4) for storing money in MySQL. It gives you exact fixed-point arithmetic with no rounding surprises, supports values up to trillions, and handles four-decimal currencies like stock prices and crypto amounts without modification. The alternative is storing values as integers in the smallest currency unit (cents, pips), which trades readability for raw speed. Never use FLOAT or DOUBLE for money.
Why DECIMAL Is the Standard Choice
DECIMAL (also called NUMERIC in SQL) stores numbers as exact fixed-point values. Unlike floating-point types, it does not approximate. When you store 19.99, MySQL stores exactly 19.9900, not 19.9900000000000002 or some other binary approximation.
Choosing precision and scale
The syntax is DECIMAL(M, D) where M is the total number of digits and D is the number of digits after the decimal point.
| Use Case | Recommended Type | Max Value | Notes |
| General commerce | DECIMAL(19, 4) | 999,999,999,999,999.9999 | Handles trillions with sub-cent precision |
| Simple USD/EUR | DECIMAL(10, 2) | 99,999,999.99 | Fine for small business apps |
| Cryptocurrency | DECIMAL(27, 18) | Up to 9 digits before decimal | Matches Ethereum's 18-decimal wei standard |
| Exchange rates | DECIMAL(12, 6) | 999,999.999999 | Six decimals for forex pip precision |
A common mistake is using DECIMAL(10, 2). That works until your application needs to handle aggregate totals, currency conversions with intermediate precision, or markets that use more than two decimal places. Starting with DECIMAL(19, 4) avoids schema migrations later.
Why FLOAT and DOUBLE Fail for Money
FLOAT and DOUBLE use IEEE 754 binary floating-point representation. They cannot represent most decimal fractions exactly. This is not a MySQL bug. It is how binary floating-point works in every language and database.
Compare with DECIMAL:
In financial applications, these small errors compound. A billing system that processes millions of transactions with floating-point arithmetic will produce totals that do not balance. Auditors and regulators will not accept "floating-point rounding" as an explanation.
The Integer Approach (Storing Cents)
An alternative to DECIMAL is storing money as integers in the smallest currency unit. For USD, that means cents. For JPY, which has no fractional unit, you store the yen amount directly.
Application-layer conversion
The conversion between cents and dollars happens in application code:
When integers make sense
Stripe, Square, and many payment processors use integer cents in their APIs. If your application integrates heavily with these services, storing cents natively avoids constant conversion at the API boundary. Integer arithmetic is also marginally faster than DECIMAL arithmetic, though the difference is negligible for most workloads.
Comparison Table
| Criteria | DECIMAL(19,4) | BIGINT (cents) | FLOAT/DOUBLE |
| Precision | Exact | Exact | Approximate |
| Arithmetic accuracy | Perfect for money | Perfect for money | Rounding errors accumulate |
| Readability in queries | Human-readable amounts | Requires division by 100 | Human-readable but misleading |
| Application complexity | Low | Medium (conversion logic) | Low |
| Storage size | 9 bytes for DECIMAL(19,4) | 8 bytes for BIGINT | 4 bytes (FLOAT) / 8 bytes (DOUBLE) |
| Suitable for money | Yes | Yes | No |
| Multi-currency support | Set scale per currency need | Must track unit per currency | No |
Handling Multiple Currencies
Real applications often handle more than one currency. Currencies differ in their number of fractional digits: USD has 2, JPY has 0, BHD (Bahraini Dinar) has 3, and ETH has 18.
If you use the integer approach, store the exponent alongside the currency so your application knows how to convert:
Indexing and Query Performance
Money columns frequently appear in WHERE, ORDER BY, and aggregate queries. Index them accordingly:
DECIMAL columns are slightly larger than integers, but the difference in index size is minimal for tables under hundreds of millions of rows.
Common Pitfalls
Using FLOAT or DOUBLE for money. The precision loss is small per operation, but it accumulates across millions of transactions and makes reconciliation impossible.
Using DECIMAL(10, 2) without thinking about growth. This caps your maximum at $99,999,999.99. If your system ever handles aggregate reports, currency conversions with intermediate values, or high-value transactions, you will hit the ceiling.
Mixing integer and decimal storage without documentation. If some tables store cents and others store dollars, developers will inevitably write a query that joins them without converting, producing numbers that are off by a factor of 100.
Ignoring currency entirely. Storing a bare number without a currency code makes it impossible to correctly handle multi-currency scenarios later. Always store the currency alongside the amount.
Performing division in SQL without controlling precision. SQL integer division truncates. If you divide an integer cents column by 100 to get dollars, use amount_cents / 100.0 (note the decimal literal) to force decimal division.
Summary
- Use
DECIMAL(19, 4)as the default for money columns in MySQL. It provides exact arithmetic and enough range for virtually any financial application. - Never use
FLOATorDOUBLEfor monetary values. Binary floating-point cannot represent most decimal fractions exactly. - Storing money as
BIGINTcents is a valid alternative, especially when integrating with payment APIs that use integer amounts. The tradeoff is additional conversion logic in your application. - Always store a currency code alongside every monetary value.
- Index money columns that appear in
WHERE,ORDER BY, or aggregate queries. - Start with more precision than you think you need. Expanding
DECIMALscale later requires anALTER TABLEthat rewrites the entire table.

