Truncate and Round giving more Decimals than expected in MySQL - mysql

I'm trying to take values in a Pervasive SQL database through an ODBC connection that represent dollar values and round them to the nearest cent. However whenever I run the TRUNCATE or ROUND functions, I get more digits after the decimal place than expected.
For example the command
select TRUNCATE(1234.12346345766,2), ROUND(1234.12346345766,2), TRUNCATE(ROUND(1234.12346345766,2),2);
returns the following
+------------------------------+---------------------------+---------------------------------------+
| TRUNCATE(1234.12346345766,2) | ROUND(1234.12346345766,2) | TRUNCATE(ROUND(1234.12346345766,2),2) |
+------------------------------+---------------------------+---------------------------------------+
| 1234.1199999999999 | 1234.1199999999999 | 1234.1099999999999 |
+------------------------------+---------------------------+---------------------------------------+
Where as what I would expect is something like
+------------------------------+---------------------------+---------------------------------------+
| TRUNCATE(1234.12346345766,2) | ROUND(1234.12346345766,2) | TRUNCATE(ROUND(1234.12346345766,2),2) |
+------------------------------+---------------------------+---------------------------------------+
| 1234.12 | 1234.12 | 1234.12 |
+------------------------------+---------------------------+---------------------------------------+
What can I do to fix this?

I figured it out myself but I didn't find any other post answering this exact question (please don't kill me if there is).
Looks like this is due to the values being stored as doubles rather than as decimal values (due to the limitations of the environment I'm working in I can't see the table schema.)
The solution that worked was to transform the data from DOUBLE to DECIMAL by wrapping the query in the CAST() function
select CAST(TRUNCATE(1234.12346345766,2) as DECIMAL(10,2));
select CAST(ROUND(1234.12346345766,2) as DECIMAL(10,2));
select CAST(TRUNCATE(ROUND(1234.12346345766,2),2) as DECIMAL(10,2));
With the following result
+-----------------------------------------------------+--------------------------------------------------+--------------------------------------------------------------+
| CAST(TRUNCATE(1234.12346345766,2) as DECIMAL(10,2)) | CAST(ROUND(1234.12346345766,2) as DECIMAL(10,2)) | CAST(TRUNCATE(ROUND(1234.12346345766,2),2) as DECIMAL(10,2)) |
+-----------------------------------------------------+--------------------------------------------------+--------------------------------------------------------------+
| 1234.12 | 1234.12 | 1234.11 |
+-----------------------------------------------------+--------------------------------------------------+--------------------------------------------------------------+
Which is close enough to what I wanted.

Related

What is the problem with rounding floats in mysql?

Actually we want to switch from float to decimal in our database. While we checked the values, if everything is correct, we figured out that mysql round has a strange behavior and we do not know why.
Table name: test
column name: test
column type: float
SQL: SELECT ROUND(test, 2) FROM test
-----------------------------------------
| test | result of round(test,2) |
-----------------------------------------
| 12.225 | 12.23 |
-----------------------------------------
| 12.125 | 12.12 |
-----------------------------------------
See explanation from documentation.
For conversion, you might get better results with CAST:
SELECT CAST(test as decimal(10,2)) FROM test

How to get MySQL command line tool to show booleans stored as BIT sensibly by default

I got a problem with selecting boolean types stored as BIT with MySQL. I know that I can get bit values shown in a sensible with with custom queries like with SELECT CAST(1=1 AS SIGNED INTEGER) or with SELECT BOOLFIELD + 0 ...
However, is there any way to get our booleans shown in a sensible way with command line client with queries like SELECT * FROM TABLE ?
UPDATE : At the moment I see only space in the results Example:
mysql> SELECT distinct foo, foo + 0 from table
+------+-------+
| foo | foo_0 |
+------+-------+
| | 0 | <-- Only space
| | 1 | <-- Space, one space less
+------+-------+
With some googling, I found some (maybe related) bugs from MySQL bug DB (http://bugs.mysql.com/bug.php?id=28422, http://bugs.mysql.com/bug.php?id=43670) but not answer or fix?
To store booleans, one really ought to use MySQL's BOOLEAN type (which is an alias for TINYINT(1), given that MySQL doesn't have real boolean types): 0 represents false and non-zero represents true.
Whilst it might feel like storing a boolean in a byte is more wasteful than in a BIT(1) column, one must remember that a few saved bits will translate into more bit operations for the CPU on data storage & retrieval; and I'm unsure whether most storage engines pad BIT columns to the next byte boundary anyway.
If you insist on using BIT type columns, you should be aware that they are returned as binary strings. The MySQL command line client (stupidly) attempts to render binary strings as textual (by applying its default character set), which is what causes the behaviour that you observe—there's no way to avoid this (other than to manipulate the field in the select list in order that it as returned as something other than a binary string, as you are already doing).
However, if you also insist on using SELECT * (which is bad practice, albeit somewhat more understandable from the command line client), you might consider defining a view in which the manipulation is performed and then SELECT from that. For example:
CREATE VIEW my_view AS SELECT foo + 0 AS foo, bar FROM my_table;
Then one could do:
SELECT * FROM my_view WHERE foo = 1 AND bar = 'wibble';
A BIT ugly, but maybe some workaround: CASE WHEN ... THEN ... END
Instead of
> select
guid,
consumed,
confirmed
from Account
where customerId = 'xxxx48' and name between xxxx and xxxx;
+--------------------------------------+----------+-----------+
| guid | consumed | confirmed |
+--------------------------------------+----------+-----------+
| xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | | |
| xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | | |
| xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | | |
| xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | | |
| xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | | |
+--------------------------------------+----------+-----------+
One could do:
> select
guid,
case when consumed then '1' when not consumed then '0' end as been_consumed,
case when confirmed then '1' when not confirmed then '0' end as been_confirmed
from Account
where customerId = 'xxxx48' and name between xxxx and xxxx;
+--------------------------------------+---------------+----------------+
| guid | been_consumed | been_confirmed |
+--------------------------------------+---------------+----------------+
| xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | 1 | 1 |
| xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | 1 | 0 |
| xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | 1 | 0 |
| xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | 1 | 1 |
| xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | 1 | 0 |
+--------------------------------------+---------------+----------------+

Round off values in 2 columns that have decimal places

Some of the value in two of my columns have decimal places for some reason, this is a bug in my code I need to sort out but its causing problems at the moment.
How can I round numbers with decimal places?
Example Data
# Table: level_3
|---------------------|
| day_start | day_end |
|-----------|---------|
| -123 | 20 |
| -650 | 234 |
| -133.042..| 104.0416|
| -581 | 123 |
|---------------------|
Expected Output
# Table: level_3
|---------------------|
| day_start | day_end |
|-----------|---------|
| -123 | 20 |
| -650 | 234 |
| -133 | 104 |
| -581 | 123 |
|---------------------|
EDIT: If it's any easier, it doesn't need to be rounded, just removed anything after and including the period.
EDIT 2: I have actually fixed my problem, I just changed the structure to INT and back which removed all the decimals! But thats for the answers, they will help others looking for this!
You have 2 simple options:
Round the values when you query the database, using ROUND, FLOOR, CEIL mysql function
e.g.:SELECT ROUND(day_start, 0) as day_start, ROUND(day_end, 0) as day_end
Round the value using php after you query the database, using round, floor or ceil
Use either the PHP floor() function, or the mysql FLOOR() function
PHP
<?php
echo floor(44.62); // will output "44"
MySQL
SELECT FLOOR(44.62);
-> 44
HOWEVER
SELECT FLOOR(-44.62);
-> -45
So, you can try something like:
SELECT IF(day_start < 0, CEIL(day_start), FLOOR(day_start)) s, IF(day_end < 0, CEIL(day_end), FLOOR(day_end)) e FROM level_3;
I'd suggest using either number_format or floor
You can try this (SQL Server)
SELECT CONVERT(INT,Day_Start), CONVERT(INT,Day_End)
SQL :
SELECT SUBSTRING (notetitle,1,4), SUBSTRING(notedescription,1,3) FROM notes
I have actually fixed my problem, I just changed the structure to INT and back which removed all the decimals! But thanks for the answers, they will help others looking for this!

Disable scientific notation in MySQL command-line client?

I have a MySQL table with many numeric columns (some INT, some FLOAT). I would like to query it with the MySQL command-line client (specifically, mysql Ver 14.14 Distrib 5.1.41, for debian-linux-gnu (x86_64) using readline 6.1), like so:
SELECT * FROM table WHERE foo;
Unfortunately, if the value of any numeric field exceeds 10^6, this client displays the result in scientific notation, which makes reading the results difficult.
I could correct the problem by FORMAT-ing each of the fields in my query, but there are many of them and many tables I would like to query. Instead I'm hoping to find a client variable or flag I can set to disable scientific notation for all queries.
I have not been able to find one in the --help or the man page, nor searching Google or this site. Instead all I find are discussions of preserving/removing scientific notation when using <insert-programming-language>'s MySQL API.
Thank you for any tips.
::edit::
Here's an example table ...
mysql> desc foo;
+--------------+-------------+------+-----+-------------------+
| Field | Type | Null | Key | Default |
+--------------+-------------+------+-----+-------------------+
| date | date | NO | PRI | NULL |
| name | varchar(20) | NO | PRI | NULL |
| val | float | NO | | NULL |
| last_updated | timestamp | NO | | CURRENT_TIMESTAMP |
+--------------+-------------+------+-----+-------------------+
and some example values ...
mysql> select * from foo where date='20120207';
+------------+--------+--------------+---------------------+
| date | name | val | last_updated |
+------------+--------+--------------+---------------------+
| 2012-02-07 | A | 88779.5 | 2012-02-07 13:38:14 |
| 2012-02-07 | B | 1.00254e+06 | 2012-02-07 13:38:14 |
| 2012-02-07 | C | 78706.5 | 2012-02-07 13:38:15 |
+------------+--------+--------------+---------------------+
Now, the actual values I loaded into the third field are:
88779.5, 1002539.25, 78706.5390625
and they can be seen exactly if I manipulate the value:
mysql> select date, name, ROUND(val, 10), last_updated from foo where ...
+------------+---+--------------------+---------------------+
| 2012-02-07 | A | 88779.5000000000 | 2012-02-07 13:38:14 |
| 2012-02-07 | B | 1002539.2500000000 | 2012-02-07 13:38:14 |
| 2012-02-07 | C | 78706.5390625000 | 2012-02-07 13:38:15 |
Something in the client seems to be enforcing that I only be allowed to see six significant figures, even though there are more in the table.
If a query such as
mysql> select ROUND(*, 2) from foo ...
were possible, that would be great! Otherwise I can't really take the time to individually wrap 100 column names in "ROUND()" whenever I need to inspect some data.
Interestingly, I occasionally use a phpMyAdmin interface to browse the contents of some of these tables, and that interface also has this 6 significant figure limitation. So it's not limited to just the CLI.
Well, after reading the documentation more thoroughly, I still can't see any reason why a client would limit itself to displaying only 6 sig figs from a FLOAT (especially when the table itself is definitely storing more).
Nonetheless, an acceptable solution (for this weary user) is to change all my tables to use DECIMAL(16,4) instead of FLOAT. Unfortunately, this makes all my numbers show up with 4 decimal places (even if they're all '0'). But at least all numbers have the same width now, and my client never displays them in scientific notation or limits the number of sig figs in its output.
Wouldn't the CAST function allow you to request that the values for a certain field are returned as DECIMAL ? Not an expert and haven't tried it, but that would be the first thing I try.
I know this is old but this helped me.. I used a view..
create view foo2 as select date, name, ROUND(val, 10) val, last_updated from foo
Then just do your queries on foo2. also works in phpmyadmin

Why MySQL adds extra digits to floats?

I have a table of prices.
Each price is a FLOAT with two digits after the dot.
From some reason, when I use the price in IF expression, the result is the same float with many additional digits:
mysql> select price, IF(1, price,0) as my_price from tbl_prices limit 10;
+-------+------------------+
| price | my_price |
+-------+------------------+
| 79.95 | 79.9499969482422 |
| 99.95 | 99.9499969482422 |
| 89.95 | 89.9499969482422 |
| 89.95 | 89.9499969482422 |
| 79.95 | 79.9499969482422 |
| 89.95 | 89.9499969482422 |
| 89.95 | 89.9499969482422 |
| 79.95 | 79.9499969482422 |
| 79.95 | 79.9499969482422 |
| 69.95 | 69.9499969482422 |
+-------+------------------+
10 rows in set (0.00 sec)
As you can see, price looks good, however the result of IF expression that returns the same price contains garbage.
Does anybody know what is the reason for this garbage, and how can I get rid of it (without using ROUND)?
Thanks in advance!
Just don't. A float is not an exact value. Use DECIMAL fields for example for a price.
Because you can't represent the .95 in floating point. This is the closest you will get. This is why float is approximate.
If you want exact decimal places, use DECIMAL
At first you should know how the floating point works.
Fortunately mysql provides DECIMAL data type, which can specify exact precision, for example:
DECIMAL( 10, 2)
Will store 10 decimal places long number and 2 digits out of that on right side, for example:
12345678.12
1.03
and so on.