I was wondering how many bytes an unsigned DECIMAL(3,2) uses in SQL. The documentation that I find online is not very clear, especially not when you have an unsigned DECIMAL.
The documentation seems pretty clear:
Values for DECIMAL columns are stored using a binary format that packs
nine decimal digits into 4 bytes. The storage requirements for the
integer and fractional parts of each value are determined separately.
Each multiple of nine digits requires 4 bytes, and any remaining
digits left over require some fraction of 4 bytes. The storage
required for remaining digits is given by the following table.
So, the integer part of DECIMAL(3, 2) has 1 digit and the fractional part has 2 digits. According to the table, 1-2 digits require one byte. Hence the total is two bytes.
The simple answer, which is usually correct or very close:
The storage requirement for DECIMAL(m,n) is m/2.
In your case 3/2 = 1.5 is very close. In this case, round up to get the correct "2".
Related
I have mysql DB with important financial data, currently the data is stored as float type and I get incorrect data due to float rounding, I want to store it as DECIMAL.
What is the safe way to convert the data in the DB without change existing data? or any another idea to solve that issue?
EDIT: Does converting from FLOAT to VARCHAR and than from VARCHAR to DECIMAL is a safe way?
Thanks in advance!
13815500 is exactly representable in FLOAT. But you are close to what Andrew talks about -- 16777217 is not exactly representable; it will be off by 1 Euro or dollar or whatever.
If you have no decimal places, your choices are
FLOAT, which messes up above 16,777,216.
DECIMAL(9,0) which can handle numbers up to about 1 billion. Caveat: If you need decimal places, say so!_
INT which peaks at about 2 billion.
INT UNSIGNED - limit about 4 billion (non-negative values only).
Each of the above datatypes mentioned above takes 4 bytes. All but the last allow for negative values. FLOAT will keep going, but lose bits at the bottom; the others "overflow".
Other options: DECIMAL(m,0) with bigger numbers (m<=64), DOUBLE (huge floating range), BIGINT (huge integral range); each take more space.
The syntax is
ALTER TABLE tablename
MODIFY col_name NEW_DATATYPE [NOT NULL];
(There is no need, and may be harm, in stepping through VARCHAR.)
General rule: Use DECIMAL for money because it is "exact"; use FLOAT for measurements (such as sensors, distance, etc)
More
If the max value is 13815500, then DECIMAL(64,56) will hold any of your numbers, and handle up to 56 decimal places. Furthermore, you can do basic arithmetic exactly on those numbers. Caution: If you will be SUMming a thousand such numbers, you need an extra 3 digits before the decimal point: DECIMAL(64,53). For summing a million numbers: DECIMAL(64,50).
If your current data is sitting in a FLOAT column, then you only have about 7 significant digits; the rest was lost as the numbers were stored. Can you recover the lost precision? If so, start over with a suitable DECIMAL. If not, then a numerical analyst will argue that you may as well stick with FLOAT. A SUM will still be good to about 6-7 significant digits. This is good enough for most uses.
You now have virtually all the knowledge of MySQL and numerical analysis; you decide what to do.
There is no safe way. Due to how floats work, 32 bit floats greater than 16777216 (or less than -16777216) need to be even, greater than 33554432 (or less than -33554432) need to be evenly divisibly by 4, greater than 67108864 (or less than -67108864) need to be evenly divisibly by 8, etc.
The infamous question about datatypes when storing money values in an SQL database.
However in these trying times, we now have currencies that have worth up to 18 decimal places (thank you ETH).
This now reraises the classic argument.
IDEAS
Option 1 BIGINT Use a big integer to save the real value, then store how many decimal places the currency has (simply dividing A by 10^B in translation)?
Option 2 Decimal(60,30) Store the datatype in a large decimal, which inevitibly will cost a large amount of space.
Option 3 VARCHAR(64) Store in a string. Which would have a performance impact.
I want to know peoples thoughts and what they are using if they are dealing with cryptocurrency values. As I am stumped with the best method for proceeding.
There's a clear best option out of the three you suggested (plus one from the comments).
BIGINT — uses just 8 bytes, but the largest BIGINT only has 19 decimal digits; if you divide by 1018, the largest value you can represent is 9.22, which isn't enough range.
DOUBLE — only has 15–17 decimal digits of precision; has all the known drawbacks of floating-point arithmetic.
VARCHAR — will use 20+ bytes if you're dealing with 18 decimal places; will require constant string↔int conversions; can't be sorted; can't be compared; can't be added in DB; many downsides.
DECIMAL(27,18) – if using MySQL, this will take 12 bytes (4 for each group of 9 digits). This is quite a reasonable storage size, and has enough range to support amounts as large as one billion or as small as one Wei. It can be sorted, compared, added, subtracted, etc. in the database without loss of precision.
I would use DECIMAL(27,18) (or DECIMAL(36,18) if you need to store truly huge values) to store cryptocurrency money values.
Hi I am creating a very big table using DECIMAL data types. its gonna be 50 million rows to start and grow from there, so I am concerned with storage. I need DECIMAL as I need exact representation, and the documentation is clear that if you want exact representation you must use DECIMAL.
The mysql manual is quite clear on DECIMAL storage reqs, stating :
As of MySQL 5.0.3, values for DECIMAL columns are represented using a binary format that packs nine decimal (base 10) digits into four bytes. Storage for the integer and fractional parts of each value are determined separately. Each multiple of nine digits requires four bytes, and the “leftover” digits require some fraction of four bytes. The storage required for excess digits is given by the following table.
Leftover Digits Number of Bytes
0 0
1 1
2 1
3 2
4 2
http://dev.mysql.com/doc/refman/5.0/en/storage-requirements.html
So that implies that a DECIMAL(12,4) would require:
8 bytes for integer portion and 2 types for the 'leftover' portion for total 10 bytes.
So 1st question is, wouldn't DECIMAL(18,4) use the same 10 bytes of storage? If I want to save storage, I would need to bump down to DECIMAL(9,4) and that's not an option for me.
IF so, 2nd question any idea if mysql processes DECIMAL(12,4) more efficiently (internally) than DECIMAL(18,4)? I dont think that question is necessarily answerable, but thought I would give it a shot! maybe someone has done some sort of benchmark...
thx.
Don
You have your calculations wrong.
If I understand correctly what the page you link to describes, a DECIMAL(12,4) would require:
The integer portion is 8 digits, so 4 bytes
The fractional part is 4 digits, so 2 bytes.
Total is 6 bytes.
For DECIMAL(18,4), which has 14 integer digits and 4 fractional digits, it would require (4+3)+(2) = 9 bytes.
Without thinking too much about it, I believe ybercube has the correct answer. Having said that, couldn't you just go ahead and easily test this yourself by creating a table and doing some inserts? "show table status" should probably have the information you need.
I need to store a large volume of small decimals numbers (3 digits before the decimal, 6 digits after the decimal).
From my understanding of the spec, this will require 8 bytes. I could store the number as an int which requires only 4 bytes and convert after retrieval using a fixed scale factor.
Is there a better alternative instead of using an int, I can't easily do any arithmetic on the numbers?
Thanks.
I do not think this is correct.
DECIMAL(9,6) should do the job.
It will require 2 bytes for the 3 digits and 3 bytes for the 6 digits according to mysql 5.1 manual. IMHO that´s 5 bytes not 8 bytes in total.
You will therefore not require a lot more memory than with the integer "hacking" you proposed. I would definitely go with decimal in your case.
No, it won't work if you are using "int" data type of MySQL. This is because integers can't handle decimal precision.
According to your question, you should be using "Fixed Point Data Types", which will benefit you in large calculations & monetary data. In MySQL, the required data type is "DECIMAL" and you can read more on it here.
The proper syntax in you case will be "DECIMAL (9, 6)", where 9 means that values can be stored with up to 9 digits in total, of which 6 digits are after the decimal point and 3 digits are before the decimal point.
Hope it helps.
What is the reason that some people from Oracle background are using DECIMAL(31,0) for integers. In MySQL it is not efficient.
Oracle implements the "INTEGER" ANSI datatype as a synonym for NUMBER(38)
For more details see "Learn Oracle: Datatypes for SQL and PL/SQL, Numbers"
However, the following table should be used as a mapping between datatype in Oracle and MySQL:
"Oracle® Database SQL Developer Supplementary Information for MySQL Migrations"
Oracle and MySQL Compared > Data Types > Comparing Data Types
The main difference, as explained here and here is that Oracle NUMBER datatype is variable-length format while MySQL DECIMAL(n) datatype used to be represented as strings that require one byte per digit or sign character (before version 5.0.3)
(Oracle NUMBER Datatype) Internal Numeric Format
Oracle Database stores numeric data in
variable-length format. Each value is
stored in scientific notation, with 1
byte used to store the exponent and up
to 20 bytes to store the mantissa. The
resulting value is limited to 38
digits of precision. Oracle Database
does not store leading and trailing
zeros. For example, the number 412 is
stored in a format similar to 4.12 x
102, with 1 byte used to store the
exponent(2) and 2 bytes used to store
the three significant digits of the
mantissa(4,1,2). Negative numbers
include the sign in their length.
Taking this into account, the column
size in bytes for a particular numeric
data value NUMBER(p), where p is the
precision of a given value, can be
calculated using the following
formula:
ROUND((length(p)+s)/2))+1
where s equals zero if the number is
positive, and s equals 1 if the number
is negative.
Zero and positive and negative
infinity (only generated on import
from Oracle Database, Version 5) are
stored using unique representations.
Zero and negative infinity each
require 1 byte; positive infinity
requires 2 bytes.
and
(MySQL) DECIMAL Data Type Changes
The storage requirements for the
integer and fractional parts of each
value are determined separately. Each
multiple of nine digits requires four
bytes, and any remaining digits left
over require some fraction of four
bytes. [...]
For example, a DECIMAL(18,9) column
has nine digits on either side of the
decimal point, so the integer part and
the fractional part each require four
bytes. A DECIMAL(20,6) column has
fourteen integer digits and six
fractional digits. The integer digits
require four bytes for nine of the
digits and three bytes for the
remaining five digits. The six
fractional digits require three bytes.