MySQL FLOOR function unexpected results - mysql

CREATE TABLE table_name (col_a double(10,2), col_b double(10,2), col_c double(10,2));
INSERT INTO table_name VALUES(36.3, 0, 6.3);
QUERY
SELECT FLOOR(36.3- 0 -6.3), FLOOR(col_a - col_b - col_c) AS calc, col_a, col_b, col_c
FROM table_name LIMIT 1;
RESULT
first selected value => FLOOR(36.3- 0 -6.3) result in 30.
second selected value => FLOOR(col_a - col_b - col_c) which is equals to FLOOR(36.3- 0 -6.3) result in 29 but i am expecting 30
Why these selects getting two different values?

This is a known problem in MySQL when using the double or float type, which are not stored internally exactly as we see them.
If you read the MySQL documentation, you will find a suggested workaround which is to use decimal instead of double. You can see in the following Fiddle that all is working as expected when using decimal(10,2) as your column types:
SQLFiddle

The values you put in the select are automatically taken as decimal and that's why the result is correct.
select 36.3 - 0 - 6.3
--Produces
30.0
Floating point types are not stored exactly, so you'll not get the exact results. Try this:
select 36.3E0 - 0E0 - 6.3E0
--Produces
29.999999999999996
and hence floor gives you 29 in the output.
From https://dev.mysql.com/doc/refman/5.5/en/floating-point-types.html
Because floating-point values are approximate and not stored as exact
values, attempts to treat them as exact in comparisons may lead to
problems. They are also subject to platform or implementation
dependencies.
And from https://dev.mysql.com/doc/refman/5.5/en/problems-with-float.html
A floating-point value as written in an SQL statement may not be the
same as the value represented internally.
As Tim advised, you should use Decimal type instead.

This is because floor(-6.3) is 7 .Hence it will become 29.
Further details you can check on https://msdn.microsoft.com/en-us/library/ms178531.aspx

Binary floating point it is based on the IEEE 754 standard for float and double.
when you insert value 36.3 into the column which has datatype double then its stores as '36.29999923706055' and for 6.3 - > '6.300000190734863'
You can convert from here Link 1 or Link 2
Now Result col_a - col_b - col_c is '29.99999904632569'.
Now you applied floor on it which give you result '29'
FLOOR() returns the largest integer value not greater than a number specified as an argument.
floor (col_a - col_b - col_c)
Returns output floor(29.99999904632569) which give you answer - > 29
As Tim advised, you should use Decimal type instead or use below query.
SELECT FLOOR(36.3- 0 -6.3),(col_a - col_b - col_c)
AS calc, col_a, col_b, col_c
FROM table_name LIMIT 1;
Output : 30

Related

I need a trigger to create id's in my sql database with a string and some zeros

I'm currently using this trigger which adds id's with 3 zeros and two zeros and then the id from the sequences table.
BEGIN
INSERT INTO sequences VALUES (NULL);
SET NEW.deelnemernr = CONCAT('ztmr16', LPAD(LAST_INSERT_ID(), 3, '0'));
END
I changed the 3 to 4 but then it didn't increment the id anymore, resulting in and multiple id error. It stayed at ztmr16000. So what can I do to add more zeros and still get the id from the sequencestable?
The MySQL LPAD function limits the number of characters returned to the specified length.
The specification is a bit unclear, what you are trying to achieve.
If I need a fixed length string with leading zeros, my approach would be to prepend a boatload of zeros to my value, and then take the rightmost string, effectively lopping off extra zeros from the front.
To format a non-negative integer value val into a string that is ten characters in length, with the leading characters as zeros, I'd do something like this:
RIGHT(CONCAT('000000000',val),10)
As a demonstration:
SELECT RIGHT(CONCAT('000000000','123456789'),10) --> 0123456789
SELECT RIGHT(CONCAT('000000000','12345'),10) --> 0000012345
Also, I'd be cognizant of the maximum length allowed in the column I was populating, and be sure that the length of the value I was generating didn't exceed that, to avoid data truncation.
If the value being returned isn't be truncated when it's inserted into the column, then what I think the behavior you observe is due to the value returned from LAST_INSERT_ID() exceeding 1000.
Note that for a non-negative integer value val, the expression
LPAD(val,3,'0')
will allow at most 1000 distinct values. LPAD (as I noted earlier) restricts the length of the returned string. In this example, to three characters. As a demonstration of the behavior:
SELECT LPAD( 21,3,'0') --> 021
SELECT LPAD( 321,3,'0') --> 321
SELECT LPAD( 54321,3,'0') --> 543
SELECT LPAD( 54387,3,'0') --> 543
There's nothing illegal with doing that. But you're going to be in trouble if you depend on that to generate "unique" values.
FOLLOWUP
As stated, the specification ...
"adds id's with 3 zeros and two zeros and then the id from the sequences table."
is very unclear. What is it exactly that you want to achieve? Consider providing some examples. It doesn't seem like there's an issue concatenating something to those first five fixed characters. The issue seems to be with getting the id value "formatted" to your specification
This is just a guess of what you are trying to achieve:
id value formatted return
-------- ----------------
1 0001
9 0009
22 0022
99 0099
333 0333
4444 4444
55555 55555
666666 666666
You could achieve that with something like this:
BEGIN
DECLARE v_id BIGINT;
INSERT INTO sequences VALUES (NULL);
SELECT LAST_INSERT_ID() INTO v_id;
IF ( v_id <= 9999 ) THEN
SET NEW.deelnemernr = CONCAT('ztmr16',LPAD(v_id,4,'0'));
ELSE
SET NEW.deelnemernr = CONCAT('ztmr16',v_id);
END IF;
END

MySQL: compare a mixed field containing letters and numbers

I have a field in the mysql database that contains data like the following:
Q16
Q32
L16
Q4
L32
L64
Q64
Q8
L1
L4
Q1
And so forth. What I'm trying to do is pull out, let's say, all the values that start with Q which is easy:
field_name LIKE 'Q%'
But then I want to filter let's say all the values that have a number higher than 32. As a result I'm supposed to get only 'Q64', however, I also get Q4, Q8 and so for as I'm comparing them as strings so only 3 and the respective digit are compared and the numbers are in general taken as single digits, not as integers.
As this makes perfect sense, I'm struggling to find a solution on how to perform this operation without pulling all the data out of the database, stripping out the Qs and parsing it all to integers.
I did play around with the CAST operator, however, it only works if the value is stored as string AND it contains only digits. The parsing fails if there's another character in there..
Extract the number from the string and cast it to a number with *1 or cast
select * from your_table
where substring(field_name, 1, 1) = 'Q'
and substring(field_name, 2) * 1 > 32

CAST DECIMAL to INT

I'm trying to do this:
SELECT CAST(columnName AS INT), moreColumns, etc
FROM myTable
WHERE ...
I've looked at the help FAQs here: http://dev.mysql.com/doc/refman/5.0/en/cast-functions.html , it says I can do it like CAST(val AS TYPE), but it's not working.
Trying to convert a decimal to int, real value is 223.00 and I want 223
You could try the FLOOR function like this:
SELECT FLOOR(columnName), moreColumns, etc
FROM myTable
WHERE ...
You could also try the FORMAT function, provided you know the decimal places can be omitted:
SELECT FORMAT(columnName,0), moreColumns, etc
FROM myTable
WHERE ...
You could combine the two functions
SELECT FORMAT(FLOOR(columnName),0), moreColumns, etc
FROM myTable
WHERE ...
A more optimized way in mysql for this purpose*:
SELECT columnName DIV 1 AS columnName, moreColumns, etc
FROM myTable
WHERE ...
Using DIV 1 is a huge speed improvement over FLOOR, not to mention string based functions like FORMAT
(graphic from Roland Bouman's blog)
mysql> SELECT BENCHMARK(10000000,1234567 DIV 7) ;
+-----------------------------------+
| BENCHMARK(10000000,1234567 DIV 7) |
+-----------------------------------+
| 0 |
+-----------------------------------+
1 row in set (0.83 sec)
mysql> SELECT BENCHMARK(10000000,1234567 / 7) ;
+---------------------------------+
| BENCHMARK(10000000,1234567 / 7) |
+---------------------------------+
| 0 |
+---------------------------------+
1 row in set (7.26 sec)
mysql> SELECT BENCHMARK(10000000,FLOOR(1234567 / 7)) ;
+----------------------------------------+
| BENCHMARK(10000000,FLOOR(1234567 / 7)) |
+----------------------------------------+
| 0 |
+----------------------------------------+
1 row in set (8.80 sec)
(*) NOTE: As pointed by Grbts, be aware of the behaviour of DIV 1 when used with non unsigned/positive values.
From the article you linked to:
The type can be one of the following values:
BINARY[(N)]
CHAR[(N)]
DATE
DATETIME
DECIMAL[(M[,D])]
SIGNED [INTEGER]
TIME
UNSIGNED [INTEGER]
Try SIGNED instead of INT
The CAST() function does not support the "official" data type "INT" in MySQL, it's not in the list of supported types. With MySQL, "SIGNED" (or "UNSIGNED") could be used instead:
CAST(columnName AS SIGNED)
However, this seems to be MySQL-specific (not standardized), so it may not work with other databases. At least this document (Second Informal Review Draft) ISO/IEC 9075:1992, Database does not list "SIGNED"/"UNSIGNED" in section 4.4 Numbers.
But DECIMAL is both standardized and supported by MySQL, so the following should work for MySQL (tested) and other databases:
CAST(columnName AS DECIMAL(0))
According to the MySQL docs:
If the scale is 0, DECIMAL values contain no decimal point or
fractional part.
use this
mysql> SELECT TRUNCATE(223.69, 0);
> 223
Here's a link
There is an important difference between floor() and DIV 1. For negative numbers, they behave differently. DIV 1 returns the integer part (as cast as signed does), while floor(x) returns "the largest integer value not greater than x" (from the manual). So : select floor(-1.1) results in -2, while select -1.1 div 1 results in -1
your can try this :
SELECT columnName1, CAST(columnName2 AS SIGNED ) FROM tableName
There's also ROUND() if your numbers don't necessarily always end with .00. ROUND(20.6) will give 21, and ROUND(20.4) will give 20.
Try cast (columnName as unsigned)
unsigned is positive value only
If you want to include negative value, then cast (columnName as signed),
The difference between sign (negative include) and unsigned (twice the size of sign, but non-negative)
1 cent:
no space b/w CAST and (expression).
i.e., CAST(columnName AS SIGNED).

how to sort varchar numeric columns by DESC or ASC?

I write ...
ORDER BY column ASC
but my column is VARCHAR and it sorts wrong like 1, 10, 2, instead of 1, 2, 10.
How can I do it to sort like 1, 2, 10?
order by
cast(column as float)
Notes:
Assumed you only have numbers in the columns. No "fish" or "bicycle"
empty strings CAST to zero
Edit: For MySQL. You can not cast to float
order by
cast(column as decimal(38,10))
You can cast to int...
order by cast(column as int)
DEMO
DECLARE #q as table(
name varchar(50),
columnn varchar(10)
)
insert into #q
VALUES('one','1'),('one','10'),('one','20'),('one','3'),('one','2'),('one','20')
select * from #q order by cast (columnn as int) desc
prints
-------------------------------------------------- ----------
one 20
one 20
one 10
one 3
one 2
one 1
So, Daniel, yes, it works :)
UPDATE:
order by cast(column as decimal(20,6))
Will cast the column values to decimal numbers with 20 digits max and 6 decimal places. Adjust it to your actual requirements.
Try this:
order by CAST(column as UNSIGNED)
i used this way
multiply it with one the query is :
ORDER BY columnname * 1 ASC
Example: Table user have value with column value [varchar(20)].
Then you can query it:
SELECT * FROM user ORDER BY value * 1
After we multiply it MySQL will treat it like a number but this way is not recommended for a heavy load.

Single quotes affecting the calculations in Select query

SELECT COUNT(*) FROM area
WHERE ROUND(SQRT(POWER(('71' - coords_x), 2) +
POWER(('97' - coords_y), 2))) <= 17
==> 51
SELECT COUNT(*) FROM area
WHERE ROUND(SQRT(POWER((71 - coords_x), 2) +
POWER((97 - coords_y), 2))) <= 17
==> 22
coords_x and coords_y are both TINYINT fields containing values in the range [1, 150]. Usually MySQL doesn't care if numbers are quoted or not.. but apparently it does in this case.
The question is just: Why?
MySQL always cares about data types. What happens is that your code relies in automatic type casting and performs math on strings (which can hold a number or not). This can lead to all sort of unpredictable results:
SELECT POW('Hello', 'World') -- This returns 1
To sum up: you need to learn and use the different data types MySQL offers. Otherwise, your application will never do reliable calculations.
Update:
One more hint:
TINYINT[(M)] [UNSIGNED] [ZEROFILL]
A very small integer. The signed range
is -128 to 127. The unsigned range is
0 to 255.
URL:
http://dev.mysql.com/doc/refman/5.1/en/numeric-type-overview.html
I hope you are not trying to store 150 in a signed tinyint column.