Im little bit stucked with my SQL query.
I've got a table with rows that can be identified by id or hash string...
id
short
title
1
asdadasdsd
foo
2
1qweqweqwe
bar
3
yxcyxcyxcy
baz
So SQL is quite easy...
SELECT * FROM table WHERE id=<identifier> OR hash=<identifier>
What I found out is that when my identifier is hash and begins with number which could be found in the id column, MYSQL returning me "wrong" row.
For example when my identifier is "1qweqweqwe" result is row 1.
I think the reason for that is it converts my hash string into integer maybe? Is there a way how to disable this behaviour?
Or the only way is to regenerate all hashes into new formats without numbers in it?
Thank you for any clarification :)
Petr
No, you do not have to regenerate the hashes. If both the id and hash match and you prefer
to pull row based on hash, then you could have the hash as the first match column condition. Basically it goes with the first match condition that is found to be true.
Also, I suppose you are already adding quotes to the hash string in the query. If not please do, as it will validate as a string then.
SELECT * FROM table WHERE hash='<identifier>' OR id=<identifier>
You seem to be passing the identifier in as a string -- because it is. But then you are comparing to a number (the id) and the string parameter is converted to a number. MySQL does so by converting the leading digits, if any.
I don't like the logic of passing in a string for an identifier, so I would really suggest that you fix the calling logic and call either:
WHERE id = <int identifier>
or:
WHERE hash = <string identifier>
But if you want to keep your current version, you can convert to a string:
WHERE CAST(id AS CHAR) = <identifier> OR hash = <identifier>
Related
I have a pretty simple table called roles:
When I ran SELECT * FROM roles WHERE role_id = "1ojosd041l"(the WHERE clause clearly didn't match with any row in the table). It somehow returned this:
Does anyone know why this is happening? My guess is that my role_id column is of type int, but I passed a string into the WHERE clause, so the str to int conversion produced something weird (like a 0 or 1).
I'm just looking for an official explanation for this. If anyone knows why this happens please let me know! Thank you!
Where MySQL expects a number, it will convert a string to a number, and ignore any leading whitespace or trailing garbage.
If you want to avoid this, do something like
SELECT * FROM roles WHERE role_id = "1ojosd041l" and concat(role_id) = "1ojosd041l"
Just the second condition would be enough but leaving the first condition in allows an index to be used.
There is somee MySQL magic happening here. MySQL sees that role_id is numeric. So it converts your string '1ojosd041l' to a number. This string isn't a number obviously, and MySQL should throw an error. But MySQL just takes as many digits as it finds from the left side of your string instead, so it gets number 1. (If your string started with a non-digit, then MySQL would even return a zero.)
The query executed should match the story_id with the provided string but when I execute the query it's giving me a wrong result. Please refer to the screenshot.
story_id column in your case is of INT (or numeric) datatype.
MySQL does automatic typecasting in this case. So, 5bff82... gets typecasted to 5 and thus you get the row corresponding to story_id = 5
Type Conversion in Expression Evaluation
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.
Now, ideally your application code should be robust enough to handle this input. If you expect the input to be numeric only, then your application code can use validation operations on the data (to ensure that it is only a number, without typecasting) before sending it to MySQL server.
Another way would be to explicitly typecast story_id as string datatype and then perform the comparison. However this is not recommended approach as this would not be able to utilize Indexing.
SELECT * FROM story
WHERE (CAST story_id AS CHAR(12)) = '5bff82...'
If you run the above query, you would get no results.
you can also use smth like this:
SELECT * FROM story
WHERE regexp_like(story_id,'^[1-5]{1}(.*)$');
for any story_ids starting with any number and matching any no of charatcers after that it wont match with story_id=5;
AND if you explicitly want to match it with a string;
In my application, I use a generic where clause to search in different fields.
My request is like this :
SELECT * FROM command WHERE id = :search OR name LIKE %:search%;
I am searching the text "2SAV".
this request return some records where name contains "2SAV" (what I want) but also the record where id=2.
If I do more tests, I notice that :
SELECT CAST("2SAV" AS SIGNED); //2
SELECT 2="2SAV"; //1
If the string start with an integer, this part of string is kept.
Have you got an option or workaround to avoid this comportment?
Thanks in advance,
In the MySQL documentation( https://dev.mysql.com/doc/refman/5.7/en/type-conversion.html) is stated:
In all other cases, the arguments are compared as floating-point (real) numbers.
I think you could cast in the query:
SELECT * FROM command WHERE id = cast(:search as char) OR name LIKE %:search%;
I have a table on my MySQL db named membertable. The table consists of two fields which are memberid and membername. The memberid field has the type of integer and uses auto_increment function starting from 2001. The membername table has the type of varchar.
The membertable has two records with the same order as described above. The records look like this :
memberid : 2001
membername : john smith
memberid : 2002
membername : will smith
I found something weird when I ran a SELECT statement against the memberid field. Running the following statement :
SELECT * FROM `membertable` WHERE `memberid` = '2001somecharacter'
It returned the first data.
Why did that happen? There's no record with memberid = 2001somecharacter. It looks like MySQL only search the first 4 character (2001) and when It's found related data, which is the returned data above, it denies the remaining characters.
How could this happen? And is there any way to turn off this behavior?
--
membertable uses innodb engine
This happens because mysql tries to convert "2001somecharacter" into a number which returns 2001.
Since you're comparing a number to a string, you should use
SELECT * FROM `membertable` WHERE CONVERT(`memberid`,CHAR) = '2001somecharacter';
to avoid this behavior.
OR to do it properly, is NOT put your search variable in quotes so that it has to be a number otherwise it'll blow up because of syntax error and then in front end making sure it's a number before passing in the query.
sqlfiddle
Your finding is an expexted MySQL behaviour.
MySQL converts a varchar to an integer starting from the beginning. As long as there are numeric characters wich can easily be converted, they are icluded in the conversion process. If there's a letter, the conversion stops returning the integer value of the numeric string read so far...
Here's some description of this behavior on the MySQL documentation Site. Unfortunately, it's not mentioned directly in the text, but there's an example which exactly shows this behaviour.
MySQL is very liberal in converting string values to numeric values when evaluated in numeric context.
As a demonstration, adding 0 causes the string to evaluated in a numeric context:
SELECT '2001foo' + 0 --> 2001
, '01.2-3E' + 0 --> 1.2
, 'abc567g' + 0 --> 0
When a string is evaluated in a numeric context, MySQL reads the string character by character, until it encounters a character where the string can no longer be interpreted as a numeric value, or until it reaches the end of the string.
I don't know of a way to "turn off" or disable this behavior. (There may be a setting of sql_mode that changes this behavior, but likely that change will impact other SQL statements that are working, which may stop working if that change is made.
Typically, this kind of check of the arguments is done in the application.
But if you need to do this in the SELECT statement, one option would be cast/convert the column as a character string, and then do the comparison.
But that can have some significant performance consequences. If we do a cast or convert (or any function) on a column that's in a condition in the WHERE clause, MySQL will not be able to use a range scan operation on a suitable index. We're forcing MySQL to perform the cast/convert operation on every row in the table, and compare the result to the literal.
So, that's not the best pattern.
If I needed to perform a check like that within the SQL statement, I would do something like this:
WHERE t.memberid = '2001foo' + 0
AND CAST('2001foo' + 0 AS CHAR) = '2001foo'
The first line is doing the same thing as the current query. And that can take advantage of a suitable index.
The second condition is converting the same value to a numeric, then casting that back to character, and then comparing the result to the original. With the values shown here, it will evaluate to FALSE, and the query will not return any rows.
This will also not return a row if the string value has a leading space, ' 2001'. The second condition is going to evaluate as FALSE.
When comparing an INT to a 'string', the string is converted to a number.
Converting a string to a number takes as many of the leading characters as it can and still be a number. So '2001character' is treated as the number 2001.
If you want non-numeric characters in member_id, make it VARCHAR.
If you want only numeric ids, then reject '200.1character'
This is more a "why" or "any pointers to documentations" question.
Today I realised that a comparison in WHERE clause takes only the first numerical part of the string if compared to an INT column. (My own conclusion)
For example:
SELECT * FROM companies c WHERE id = '817m5'
will still return companies with id = 817.
It does make sense but I'm failing to find this documented anywhere to see if my conclusion is correct and if there are additional things to know about this exact behaviour. WHERE, INT type, comparison documentation? Where? How is it called?
Thanks!
This is the comparison:
WHERE id = '817m5'
and id is an integer.
So, MySQL has to compare an integer to a string. Well, it can't do that directly. It either has to convert the string to a number or the number to the string. MySQL converts the string to a number, which it does by converting the leading numeric characters. So, the '817m5' becomes 817.
Here is the exact quote:
To cast a string to a numeric value in numeric context, you normally
do not have to do anything other than to use the string value as
though it were a number:
mysql> SELECT 1+'1'; -> 2