Unexpected result when combining IFNULL with TRUNCATE - mysql

The following query returns 0.000 when I expected it to return 0.
SELECT IFNULL(TRUNCATE(NULL, 3), 0) FROM DUAL
Why is that?
Breaking it apart works as expected and described in the TRUNCATE function documentation and IFNULL docs :
SELECT TRUNCATE(NULL, 3) FROM DUAL
returns null.
SELECT IFNULL(null, 0) FROM DUAL
this returns 0. So why do I get 0.000 when nesting them?

The type of TRUNCATE(NULL,n) is DOUBLE. This can be seen by running mysql with the --column-type parameter:
$mysql -u root --column-type testdb
mysql> SELECT(TRUNCATE(NULL,3));
Field 1: `(TRUNCATE(NULL,3))`
Catalog: `def`
Database: ``
Table: ``
Org_table: ``
Type: DOUBLE
Collation: binary (63)
Length: 20
Max_length: 0
Decimals: 3
Flags: BINARY NUM
+--------------------+
| (TRUNCATE(NULL,3)) |
+--------------------+
| NULL |
+--------------------+
1 row in set (0,00 sec)
According to the IFNULL documentation page:
The default result value of IFNULL(expr1,expr2) is the more “general” of the two expressions, in the order STRING, REAL, or INTEGER
Therefore your result is 0.000, the 0 as DOUBLE truncated to 3 decimal places.

Your expectation is wrong. TRUNCATE(NULL, 3) is going to return a decimal value with three decimal places. Although the value is NULL, NULL has a type associated with it. The type is integer by default. But this is not a default situation.
So, 0 is converted to a decimal with three decimal places.
EDIT:
To understand what I mean, consider this code:
create table t as
select truncate(NULL, 3) as x;
describe t;
You will see that the column has a precision of "3". The NULL value is not typeless. You can see this on SQL Fiddle.

Related

Mysql enum not searching record without quote value

I have table field with enum enum('0','1','2','3','4','5','6').
In query when I try with IN (1) it is not working but when I use IN ("1") it is working.
If I use In (1,2) it is working.
So when there is one value in "IN" clause then it should work without quote
ENUM values are defined and stored as strings. They can be accessed via their string value or via their numeric index value (which starts at 1 and not 0).
Your enum values start at '0' which would have a numeric index value of 1. So, it would be possible to get some data back from your query using IN(1,2) but those numbers will be treated as the index and you will actually get data returned for records where the enum values are '2' and '3' (probably not what you think you are getting back).
IN(1) doesn't work because you have no data with an index value of 1 (emum value = '0')
IN('1') does work because you do have data with an enum value of '1'
Try this for a practical example
http://sqlfiddle.com/#!9/3993b/1
or read the section on Handling of Enumeration Literals in the mysql documentation
You can use values as string or integer, IN() function support both types.
The IN operator allows you to specify multiple values in a WHERE clause.
Usually IN() function is using for multiples values but it does support single value also. For me all queries are working
SELECT * FROM `your_table` WHERE `id` IN('4') // String Number
SELECT * FROM `your_table` WHERE `id` IN('4','2','1') // String Multiple Number
SELECT * FROM `your_table` WHERE `id` IN(1) // Single Integer
SELECT * FROM `your_table` WHERE `id` IN(1,2,4) // Multiple Integer
Avoid using number as 'enumeration values'
Doucumenation quotes-We strongly recommend that you do not use numbers as enumeration values, because it does not save on storage over the appropriate TINYINT or SMALLINT type, and it is easy to mix up the strings and the underlying number values (which might not be the same) if you quote the ENUM values incorrectly. If you do use a number as an enumeration value, always enclose it in quotation marks. If the quotation marks are omitted, the number is regarded as an index. See Handling of Enumeration Literals to see how even a quoted number could be mistakenly used as a numeric index value.'
Link- https://dev.mysql.com/doc/refman/5.7/en/enum.html#enum-limits
enum supports accessing values either by numeric index of values or values that are directly quoted.
As per MySQL documentation on ENUM:
... strongly recommend that you do not use numbers as enumeration values,
because it does not save on storage over the appropriate TINYINT or SMALLINT type,
and it is easy to mix up the strings and the underlying number values
(which might not be the same) if you quote the ENUM values incorrectly.
If you do use a number as an enumeration value, always enclose it in quotation marks.
If the quotation marks are omitted, the number is regarded as an index.
Examples:
mysql> drop table if exists so_q48786040;
mysql> CREATE TABLE so_q48786040(e ENUM('6', '5', '4', '3', '2', '1', '0'));
mysql> INSERT INTO so_q48786040 VALUES( 2 ), ( 4 ), ( 7 ), ( '6' );
Query OK, 4 rows affected (0.05 sec)
Records: 4 Duplicates: 0 Warnings: 0
Selecting all rows, returns values 5,3,0,6
mysql> SELECT * FROM so_q48786040;
+------+
| e |
+------+
| 5 |
| 3 |
| 0 |
| 6 |
+------+
4 rows in set (0.00 sec)
Selecting at specific indices. Results will be in the order of enum indices defined.
mysql> SELECT * FROM so_q48786040 WHERE e IN( 7, 4 );
+------+
| e |
+------+
| 3 | -- <--- at index 4
| 0 | -- <--- at index 7
+------+
2 rows in set (0.00 sec)
Selecting at specific quoted literals. Results will be in the order of enum indices defined.
mysql> SELECT * FROM so_q48786040 WHERE e IN( '6', '3', '0' );
+------+
| e |
+------+
| 3 |
| 0 |
| 6 |
+------+
3 rows in set (0.00 sec)
Selecting a non existing entry. Returns empty set as '4' was not inserted
mysql> SELECT * FROM so_q48786040 WHERE e IN( '4' );
Empty set (0.00 sec)
NOTE:
It is suggested to define empty literals at index 0.
The index of the NULL value is NULL.

mysql converting bit to char not working

I was trying to feed a result of a query as a parameter for another query and all was working fine except this field that has a datatype of bit. so i tried to convert the value of the field using convert() and cast() but it seems to be not working as its returning this wierd symbol of a small rectange which hava three 0's and a 1. so can anyone tell me why this is happening and how to fix it , here is my query
select CONVERT(isMale , char(5)) from person;
and the thing is it gives me the correct answer when i dont use the convert but since am giving this result to another query as a parameter it causing me the problem.
you can use BIN function like this:
SELECT BIN(isMale +0) from person;
sample
MariaDB [yourschema]> SELECT BIN(b'1001' +0) ;
+-----------------+
| BIN(b'1001' +0) |
+-----------------+
| 1001 |
+-----------------+
1 row in set (0.00 sec)
MariaDB [yourschema]>
Here some stuff from MariaDB Manual:
Description
Converts numbers between different number bases. Returns a
string representation of the number N, converted from base from_base
to base to_base.
Returns NULL if any argument is NULL, or if the second or third
argument are not in the allowed range.
The argument N is interpreted as an integer, but may be specified as
an integer or a string. The minimum base is 2 and the maximum base is
36. If to_base is a negative number, N is regarded as a signed number. Otherwise, N is treated as unsigned. CONV() works with 64-bit
precision.
Some shortcuts for this function are also available: BIN(), OCT(),
HEX(), UNHEX(). Also, MariaDB allows binary literal values and
hexadecimal literal values.
BIN is a short form from CONV(value,from,to) where you can convert from base to base
so binary 1001 = 9 as int
here i give the value in decimal (14) and convert it from base 10 to base 2
MariaDB [yourschema]> SELECT CONV(14,10 ,2);
+-----------------+
| CONV(14,10 ,2) |
+-----------------+
| 1110 |
+-----------------+
1 row in set (0.00 sec)
so, if you want to have 0 on the left you can add a value like this
MariaDB [yourschema]> SELECT CONV(8192 + 14,10 ,2);
+------------------------+
| CONV(8192 + 14,10 ,2) |
+------------------------+
| 10000000001110 |
+------------------------+
1 row in set (0.00 sec)
and then you can get n chars from right:
MariaDB [yourschema]> SELECT RIGHT(CONV(8192 + 14,10 ,2),8);
+---------------------------------+
| RIGHT(CONV(8192 + 14,10 ,2),8) |
+---------------------------------+
| 00001110 |
+---------------------------------+
1 row in set (0.40 sec)
MariaDB [yourschema]>
I think you want to use CAST
select CAST(isMale as CHAR) from person;
seeing #Bernd Buffen answer i tried using the convert with +0 and it works , eventhough i dont know why
select CONVERT(isMale +0, char(5)) from person;

MySQL Precison Issues in DECIMAL NUMERIC data type

In writing a function for scientific application, I ran into issues. I traced it back to MySQL's lack of precison.
Here is the page from the official documentation which claims that The maximum number of digits for DECIMAL is 65 - http://dev.mysql.com/doc/refman/5.6/en/fixed-point-types.html . It also describes how the value will be rounded if it exceeds the specified precison.
Here is reproducible code (a mysql stored function) to test it -
DELIMITER $$
DROP FUNCTION IF EXISTS test$$
CREATE FUNCTION test
(xx DECIMAL(30,25)
)
RETURNS DECIMAL(30,25)
DETERMINISTIC
BEGIN
DECLARE result DECIMAL(30,25);
SET result = 0.339946499848118887e-4;
RETURN(result);
END$$
DELIMITER ;
If you save the code above in a file called test.sql, you can run it by executing the following in mysql prompt -
source test.sql;
select test(0);
It produces the output -
+-----------------------------+
| test(0) |
+-----------------------------+
| 0.0000339946499848118900000 |
+-----------------------------+
1 row in set (0.00 sec)
As you can see, the number is getting rounded at the 20th digit, and then five zeroes are being added to it to get to the required/specified precison. That is cheating.
Am I mistaken, or is the documentation wrong?
This happens because mysql treats 0.339946499848118887e-4 as float and treats 0.0000339946499848118887 as fixed point.
mysql> select cast( 0.339946499848118887e-4 as DECIMAL(30, 25));
+----------------------------------------------------+
| cast( 0.339946499848118887e-4 as DECIMAL(30, 25)) |
+----------------------------------------------------+
| 0.0000339946499848118900000 |
+----------------------------------------------------+
1 row in set (0.00 sec)
mysql> select cast( 0.0000339946499848118887 as DECIMAL(30, 25));
+-----------------------------------------------------+
| cast( 0.0000339946499848118887 as DECIMAL(30, 25)) |
+-----------------------------------------------------+
| 0.0000339946499848118887000 |
+-----------------------------------------------------+
1 row in set (0.00 sec)
As described in the mysql documentation on precision math - expression handling -
If any approximate values are present, the expression is approximate and is evaluated using floating-point arithmetic.
Quoting from, the documentation on numerical types,
Two numbers that look similar may be treated differently. For example, 2.34 is an exact-value (fixed-point) number, whereas 2.34E0 is an approximate-value (floating-point) number.
I don't know anything about SQL, but my guess would be this line:
SET result = 0.339946499848118887e-4;
If MySQL is anything like other languages I know, then this will first evaluate the right-hand side, and then assign the value to result. No matter what type result is declared to be or what precision it's declared to have, it wouldn't matter if the right-hand side has already lost precision when being evaluated. This is almost surely what is happening here.
I can reproduce your results, but If I change that line to
SET result = cast('0.339946499848118887e-4' as decimal(30, 25));
(casting from a string instead of from a floating-point constant of unspecified precision) then I correctly get
+-----------------------------+
| test(0) |
+-----------------------------+
| 0.0000339946499848118887000 |
+-----------------------------+
1 row in set (0.00 sec)
as desired. So that's your fix.
BTW, the documentation that scale in DECIMAL(precision, scale) cannot be greater than 30 seems to be in section 12.19.2. DECIMAL Data Type Changes:
The declaration syntax for a DECIMAL column is DECIMAL(M,D). The
ranges of values for the arguments in MySQL 5.6 are as follows:
M is the maximum number of digits (the precision). It has a range of 1
to 65. (Older versions of MySQL permitted a range of 1 to 254.)
D is the number of digits to the right of the decimal point (the
scale). It has a range of 0 to 30 and must be no larger than M.

MySQL adding leading numbers to existing IDs in a column

I got a mysql database column named country_id, like:
country_id
----------
1
2
59
435
2714
What I'm trying to accomplish now is to add leading zero's to each ID, so the result would be:
country_id
----------
0001
0002
0059
0435
2714
Each ID should have max 4 digits. That's it.
Is there any SQL statement I could use in PHPmyAdmin to update this country_id column in the way described above?
Best regards!
Declare ZEROFILL on the column:
mysql> create table foobar (nr integer(4) ZEROFILL);
Query OK, 0 rows affected (0.31 sec)
mysql> INSERT INTO foobar VALUES (1),(12),(123),(1234),(12345);
Query OK, 5 rows affected (0.05 sec)
Records: 5 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM foobar;
| 0001
| 0012
| 0123
| 1234
| 12345
If country_id column is character datatype (NOT numeric), then you could prepend zeros and take the rightmost portion:
UPDATE foo SET country_id = RIGHT(CONCATENATE('0000',country_id),4)
UPDATE foo SET country_id = LPAD(country_id, 4, '0')
(NOTE: Either of those statements will result in data loss, if any value of country_id is longer than 4 characters... the first gets the righmost characters, the second will rth get the four leftmost characters, if country_id is over four characters. If a value has e a leading '-' character, that will result in an odd looking value e.g. '00-4'. The LTRIM function is available to remove leading spaces.
If, on the other hand, country_id is a numeric datatype, then you can't really add leading zeros.
For an integer type, you can specify a display length and the ZEROFILL option, e.g.
country_id INT(4) ZEROFILL
But it's up to the client application to make use of the length modifier and the ZEROFILL attribute to do the specified formatting, there's not really anything being done "in the database" with those attributes. (The MySQL command line client will observe those settings, and display the value zero filled to a max of four characters. But other clients are not required to do that.)
You could also cast that value to character and pad it with leading '0' characters, in a query:
SELECT LPAD(country_id, 4, '0') AS country_id
But note that's going to return a character type, not a numeric.
SELECT LPAD(country_id, 4, '0')
FROM your_table
If you can change your DB structure use ZEROFILL

Mysql Like + Wild Card vs Equals Operator

I recently just fixed a bug in some of my code and was hoping someone could explain to me why the bug occurred.
I had a query like this:
SELECT * FROM my_table WHERE my_field=13
Unexpectedly, this was returning rows where my_field was equal to either 13 or 13a. The fix was simple, I changed the query to:
SELECT * FROM my_table WHERE my_field='13'
My question is, is this supposed to be the case? I've always thought that to return a similar field, you would use something like:
SELECT * FROM my_table WHERE my_field LIKE '13%'
What is the difference between LIKE + a Wild Card vs an equals operator with no quotes?
This statement returns rows for my_field = '13a':
SELECT * FROM my_table WHERE my_field=13
Because MySQL performs type conversion from string to number during the comparison, turning '13a' to 13. More on that in this documentation page.
Adding quotes turns the integer to a string, so MySQL only performs string comparison. Obviously, '13' cannot be equal to '13a'.
The LIKE clause always performs string comparison (unless either one of the operands is NULL, in which case the result is NULL).
My guess would be that since you didn't enclose it in quotes, and the column was a char/varchar column, MySQL tried to do an implicit conversion of the varchar column to an int.
If one of the rows in that table contained a value that couldn't be converted to an int, you would probably get an error. Also, because of the conversion, any indexes you might have had on that column would not be used either.
This has to do with types and type conversion. With my_field=13 , 13 is an integer, while my_field is in your case likely some form of text/string. In such a case, mysql will try to convert both to a floating point number and compare those.
So mysql tries to convert e,g, "13a" to a float, which will which be 13, and 13 = 13
In my_field = '13' , both operands are text and will be compared as text using =
In my_field like '13%' both operands are also text and will be compared as such using LIKE, where the special % means a wildcard.
You can read about the type conversion mysql uses here.
This is because the MySQL type conversion works this way. See here: http://dev.mysql.com/doc/refman/5.0/en/type-conversion.html
It releases a warning as well. see the code below
mysql> select 12 = '12bibo';
+---------------+
| 12 = '12bibo' |
+---------------+
| 1 |
+---------------+
1 row in set, 1 warning (0.00 sec)
mysql> show warnings;
+---------+------+--------------------------------------------+
| Level | Code | Message |
+---------+------+--------------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: '12bibo' |
+---------+------+--------------------------------------------+
1 row in set (0.00 sec)
Looks like someone raised a bug as well: http://bugs.mysql.com/bug.php?id=42241