Strange behavior querying an Int field with a String in MySQL - mysql

Using MySQL 5.5.60.
I'm running into some peculiar behavior when running select queries in Mysql. I have a table list whose schema looks like this:
+-----------------------+--------------+------+-----+-----------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------------+--------------+------+-----+-----------------+----------------+
| list_id | int(11) | NO | PRI | NULL | auto_increment |
| vendor_id | int(11) | NO | MUL | NULL | |
| referrer_id | int(11) | NO | | 0 | |
...
If I run this query
mysql> select * from list where list_id = "1946"\G
Everything works as it should and the list with id 1946 is returned. Here is where it gets weird. If I change my query to look like this:
mysql> select * from list where list_id = "1946dhkdf"\G
It still returns list 1946! Clearly MySQL somehow cast off the dhkdf part and uses the 1946 portion only. So does it try to cast that value to an Integer that way? Why then does this query return and empty set?
mysql> select * from list where list_id = "xq1946dhkdf"\G
I can't seem to find any documentation explaining this behavior. Can someone shed some light on it?

You are seeing MySQL's somewhat complex casting rules at work here. When trying to compare an integer column against a string literal, either one has to be cast to integer, or the other to string. In this case, MySQL will try to cast the string literal to an integer, to match the type of the column. But, in this case, it can't cast the entire string literal to an integer, since it contains characters. Therefore, the casting rules kick in, which state that if the first N characters of the string be numeric, then use only that leading number. So, as a result the following query:
select * from list where list_id = "1946dhkdf";
will return the same result set as:
select * from list where list_id = "1946";

Related

MySQL select query when id is uuid format

the id in MySQL table is uuid, and no id is 1.
A. My query is:
select id, name from xxx_table where id=1
and I get the result:
+--------------------------------------+----------------------+
| id | name |
+--------------------------------------+----------------------+
| 1bdf0336-c5bf-4245-8897-dbda3bf9e202 | xxxxxxxxxxxxxxxxxxxx |
+--------------------------------------+----------------------+
that's not what I want. this id is not 1.
B. my new query:
select id, name from xxx_table where id='1'
and I get Empty set, that is what I want.
What I'm confused is, if it's the format issue of id, why int(1) can match uuid(1bdf0336-c5bf-4245-8897-dbda3bf9e202)?
================================================================
OK. Follow the suggestion of Luuk, when I check
show create table xxx_table;
and I get:
| Table | Create Table |
| xxx_table | CREATE TABLE `xxx_table` (`id` varchar(64) NOT NULL,
...,
PRIMARY KEY (`id`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8
MySQL plays loose with type conversion. When implicitly converting a char to a number.
When an operator is used with operands of different types, type conversion occurs to make the operands compatible. Some conversions occur implicitly.
For example, MySQL automatically converts strings to numbers as necessary, and vice versa.
For example, there is a query like below, you will get result 2 instead of an error from the query.
because Mysql will convert the query like 1 + 1 implicitly.
Query 1:
select '1bdf0336-c5bf-4245-8897-dbda3bf9e202' + 1 res
Results:
| res |
|-----|
| 2 |
this query will compare full string which equals to 1 string type
select id, name from xxx_table where id='1'
The following rules describe how conversion occurs for comparison operations:
1 - If one or both arguments are NULL, the result of the comparison is NULL, except for the NULL-safe <=> equality comparison operator. For NULL <=> NULL, the result is true. No conversion is needed.
2 - If both arguments in a comparison operation are strings, they are compared as strings.
3 - If both arguments are integers, they are compared as integers.
4 - Hexadecimal values are treated as binary strings if not compared to a number.
5 - If one of the arguments is a TIMESTAMP or DATETIME column and the other argument is a constant, the constant is converted to a timestamp before the comparison is performed. This is done to be more ODBC-friendly. Note that this is not done for the arguments to IN()! To be safe, always use complete datetime, date, or time strings when doing comparisons. For example, to achieve best results when using BETWEEN with date or time values, use CAST() to explicitly convert the values to the desired data type.
6 - A single-row subquery from a table or tables is not considered a constant. For example, if a subquery returns an integer to be compared to a DATETIME value, the comparison is done as two integers. The integer is not converted to a temporal value. To compare the operands as DATETIME values, use CAST() to explicitly convert the subquery value to DATETIME.
7 - If one of the arguments is a decimal value, comparison depends on the other argument. The arguments are compared as decimal values if the other argument is a decimal or integer value, or as floating-point values if the other argument is a floating-point value.
8 - In all other cases, the arguments are compared as floating-point (real) numbers.
type-conversion
After the SELECT ... you will see x warnings. Use SHOW WARNINGS to find out about those warnings:
mysql> select * from xxx_table where id=12;
+--------------------------------------+
| id |
+--------------------------------------+
| 12a1c7d5-6dfa-11ec-9124-309c23b7280c |
+--------------------------------------+
1 row in set, 5 warnings (0.00 sec)
mysql> show warnings;
+---------+------+--------------------------------------------------------------------------+
| Level | Code | Message |
+---------+------+--------------------------------------------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: '12a1c7d5-6dfa-11ec-9124-309c23b7280c' |
This can also be seen when doing:
mysql> select id, cast(id as unsigned) from xxx_table;
+--------------------------------------+----------------------+
| id | cast(id as unsigned) |
+--------------------------------------+----------------------+
| 12a1c7d5-6dfa-11ec-9124-309c23b7280c | 12 |
| 13392fc5-6dfa-11ec-9124-309c23b7280c | 13392 |
| 13ad01fd-6dfa-11ec-9124-309c23b7280c | 13 |
| 1425df26-6dfa-11ec-9124-309c23b7280c | 1425 |
| 14a139e8-6dfa-11ec-9124-309c23b7280c | 14 |
+--------------------------------------+----------------------+
5 rows in set, 5 warnings (0.00 sec)
P.S. The long story is about type conversion, see other answer.
The other answers have dealt with the type-conversion in plenty of detail so I thought I would suggest that you look at the other issue. Why are you storing UUID in a varchar? It may be easy but it is very inefficient as you significantly increase the size of all your indices. Storing them in a BINARY(16) would make more sense.
CREATE TABLE `uuid_tests` (
`uuid` BINARY(16) NOT NULL PRIMARY KEY,
`string` CHAR(36) NOT NULL
);
INSERT INTO uuid_tests VALUES
(UUID_TO_BIN('1bdf0336-c5bf-4245-8897-dbda3bf9e202'), '1bdf0336-c5bf-4245-8897-dbda3bf9e202');
SELECT * FROM uuid_tests WHERE uuid = UUID_TO_BIN('1bdf0336-c5bf-4245-8897-dbda3bf9e202');
SELECT * FROM uuid_tests WHERE uuid = 1;
SELECT * FROM uuid_tests WHERE string = '1bdf0336-c5bf-4245-8897-dbda3bf9e202';
SELECT * FROM uuid_tests WHERE string = 1;
As you are using UUIDs as PK it is worth reading about the second argument to both BIN_TO_UUID and UUID_TO_BIN

mysql int column matching against a string

I have the following query and result:
mysql> SELECT item_id FROM phppos_items WHERE item_id = '5CG4500RRL';
+---------+
| item_id |
+---------+
| 5 |
+---------+
item_id is an int(11) primary key
How do I prevent this from matching? It looks like it is somehow becoming 5 when matching.
I still want to run this code so I don't have to change a lot of logic so I would prefer to keep it in mysql to do a strict comparison if possible.
I can be done by several methods. For example:
SELECT item_id
FROM phppos_items
WHERE '5CG4500RRL' REGEXP '^[0-9]+$' AND item_id = '5CG4500RRL';
Here we check is input value digits only and it equal to item_id.
Here you can find more options to check input value.

Moving hex data from a varchar type field to bigint type (mysql)

I am trying to insert data from one table into another, and each table has an 'id' field that should be the same, but is stored different datatype. This 'id' field should represent the same unique value, allowing me to update from one to another.
In one table (the new.table one), the 'id' is stored as datatype varchar(35) and in the old.table it is datatype bigint(20) -- I believe this older table represents the integer version of the hex value stored in the new one. I am trying to update data from the new.table back into the old.table
After searching about this for a while
When I try this simple mysql update query it fails:
INSERT INTO old.table (id, field2)
SELECT CAST(CONV(id,16,10) AS UNSIGNED INTEGER), field2
FROM new.table;
It fails with this error:
Out of range value for column 'id' at row 1
I have also tried a simple
SELECT CAST(CONV(id, 16,10) AS UNSIGNED INTEGER) from new.table;
And the result is all the same integer mostly, but each hex value in new.table is unique. I've google this for two days, and could really use to help to figure out what is wrong. Thanks.
EDIT: Some of the example data from console of output of SELECT ID from new.table:
| 1d2353560110956e1b3e8610a35d903a |
| ec526762556c4f92a3ea4584a7cebfe1.11 |
| 34b8c838c18a4c5690514782b7137468.16 |
| 1233fa2813af44ca9f25bb8cac05b5b5.16 |
| 37f396d9c6e04313b153a34ab1e80304.16 |
The problem id is too high values.
MySQL will return limit-value when overflow happened.
Query 1:
select CONV('FFFFFFFFFFFFFFFF1',16,10)
Results:
| CONV('FFFFFFFFFFFFFFFF1',16,10) |
|---------------------------------|
| 18446744073709551615 |
Query 2:
select CONV('FFFFFFFFFFFFFFFF',16,10)
Results:
| CONV('FFFFFFFFFFFFFFFF',16,10) |
|--------------------------------|
| 18446744073709551615 |
I would suggest you, Implement the logic algorithm for id in your case in a function instead of use CONV function.
EDIT
I would use a variable to make new row number and insert to old table.
CREATE TABLE new(
Id varchar(35)
);
insert into new values ('1d2353560110956e1b3e8610a35d903a');
insert into new values ('ec526762556c4f92a3ea4584a7cebfe1.11');
insert into new values ('34b8c838c18a4c5690514782b7137468.16');
insert into new values ('1233fa2813af44ca9f25bb8cac05b5b5.16');
insert into new values ('37f396d9c6e04313b153a34ab1e80304.16');
CREATE TABLE old(
Id bigint(20),
val varchar(35)
);
INSERT INTO old (id, val)
SELECT rn, id
FROM (
SELECT *,(#Rn:=#Rn +1) rn
FROM new CROSS JOIN (SELECT #Rn:=0) v
) t1
Query 1:
SELECT * FROM old
Results:
| Id | val |
|----|-------------------------------------|
| 1 | 1d2353560110956e1b3e8610a35d903a |
| 2 | ec526762556c4f92a3ea4584a7cebfe1.11 |
| 3 | 34b8c838c18a4c5690514782b7137468.16 |
| 4 | 1233fa2813af44ca9f25bb8cac05b5b5.16 |
| 5 | 37f396d9c6e04313b153a34ab1e80304.16 |

How to compare ids of different formats across databases

Problem: I need a way to compare ids of a type varchar to ids of a type int.
Background: I have a list of ids that almost map to the ids in my table. I have ~10k ids, but I suspect there are only 3-5 variations to clean up.
The tables I'm working with could be simplified as follows. A big table of articles with good ids, and a temp table I've dumped all the dirty ids into. I'd like to update my temp table with the correct id whenever I'm able to make a match
licensing.articles
+----------+
| id (int) |
+----------+
| 1000 |
| 1001 |
| 1002 |
+----------+
tempDB.ids
+-------------------+----------------+
| id_dirty (string) | id_clean (int) |
+-------------------+----------------+
| 1000Z | |
| R1001 | |
| 1002 | |
+-------------------+----------------+
So my first query is the simple version: for records that share the same id between the licensing.articles table and the tempDB.ids table, I want to populate tempDB.ids.id_clean with the good id. (In my example, there is one shared id (1002), but in reality there's probably ~3k of them.)
When I try something like this:
UPDATE tempDB.ids AS dirty
JOIN licensing.articles clean ON clean.id = CAST(dirty.id_dirty as unsigned)
SET dirty.id_clean = clean.id
WHERE isnull(dirty.id_clean);
I get an error message Error : Truncated incorrect INTEGER value: '1000Z'. That makes sense; presumably it is failing to convert '1000Z' to an integer.
So how can I say
FOR ONLY tempDB.ids.id_dirty values that can be successfully cast to an int
SELECT the matching record from licensing.articles
AND copy licensing.articles.id to tempDB.ids.id_clean

MySQL Query Anomaly

i faced a unique problem by accident
But before that i want to show you a table structure
td_category
|---------------------------------------------------------------------|
| category_id | category_title | category_slug | p_cid |
|---------------------------------------------------------------------|
| 1 | Shirts | 1-Shirts | 0 |
|---------------------------------------------------------------------|
| 2 | Jeans | 2-Jeans | 0 |
|---------------------------------------------------------------------|
Now,
category_id is INT and auto-increment value
category_title is VARCHAR
category_slug is VARCHAR
Now what i amdoing is that, by mistake i wrote a query
SELECT * FROM td_category WHERE category_id = '2-Jeans'
and instead of giving me any error it displayed the 2nd tuple
Isn't it supposed to throw an error??
please can anybody clarify?
mysql performs implicit conversion for int datatype due to which '2-Jeans' is treated as 2-0 (since Jeans is not an int type and is defaulted to 0 for compatibility as described in the docs here)
Hence the final query as the parser interprets is as below:
SELECT * FROM td_category WHERE category_id = 2;
The following query will take id as 2 which is your first character and display second record
SELECT * FROM td_category WHERE category_id = '2-Jeans'
Try this query which will return first record
SELECT * FROM td_category WHERE category_id = '1-Jeans'
2-jeans is treated as 2 so return second record and 1-jeans is treated as 1 so return first record.
Check Manual for auto casting in mysql.