Automatic MySQL data type casting - mysql

I just happened upon an interesting case of data type casting in MySQL. Consider the following queries:
SELECT * FROM (SELECT 0 AS col) AS t WHERE t.col=123; #Yields 0 rows
SELECT * FROM (SELECT 0 AS col) AS t WHERE t.col="123"; #Yields 0 rows
SELECT * FROM (SELECT 0 AS col) AS t WHERE t.col="0"; #Yields 1 row, col=0
SELECT * FROM (SELECT 0 AS col) AS t WHERE t.col="abc"; #Yields 1 row, col=0
Lines 1, 2, and 3 seem logical to me. But on line 4, why, oh why, dear SQL, do you so eagerly cast "abc" to be equal to 0?!
I mean, I get it - "abc" isn't an integer, so 0 makes the most sense... Is there a scenario in which this behavior is actually useful? As far as I can tell, it likely just leads to bugs (as it did on our application)...
Perhaps there's a MySQL "mode" that enables warnings for automatic type-casting like this?

MySQL does implicit type casting for strings in a numeric context. The leading numeric characters of the string are converted to a number, so a string such as 'abc' gets converted to 0.
This can be very handy because this conversion does not cause an error (an explicit conversion would).
The moral is simple: When comparing constants to columns, make the column the same type as the column. That is, don't compare strings and numbers, lest something unexpected happen.

This is definitely the way MySQL works.
When you use a comparison that compares a numeric object to a string constant, the string gets cast as an integer. MySQL tries to interpret the string as an number, like this:
'0123abc' gets the value 123.
'1abc' gets the value 1.
'abc' gets the value 0.
What use is this? It comes in handy in ORDER BY clauses if you need numeric text strings ordered in numeric order with '112abc' after '12abc'.

Related

Mysql gives me unrelated results

I am new in Mysql and have a table where I'm going to select based on an integer column, the problem is that when I use an string on this column! I got no error but it gives me back all rows. for example:
SELECT * FROM `News` WHERE Cat='hello' order by id desc limit 20
It gives me 20 rows! what's wrong? did I do anything wrong or it's because of something else?
This is normal behavior for MySql, because in this expression:
Cat='hello'
what happens is an implicit conversion of the string literal 'hello' to INTEGER and as it is described in Type Conversion in Expression Evaluation the result of this conversion is 0, so the expression is equivalent to:
Cat=0
If you want to prevent this conversion you could instead convert the column Cat to string:
WHERE CONVERT(Cat, CHAR) = 'hello'
This way the comparison of Cat and 'hello' will be alphanumerical and will fail.
But if you pass a valid integer, then the correct result will be returned.

Querying a string in a int field returns all rows containing 0 in that field

I'm doing a simple search in all the fields of a table, but when I query an integer field it is returning me all the rows that have a 0 there. What is weird for me is that this doesn't happens with the id field, which doesn't retrieve anything in the case that it is a word (expected behaviour).
SELECT * FROM seats WHERE number = "2" - this works
SELECT * FROM seats WHERE number = "hello" - this brings all the zeros
SELECT * FROM seats WHERE id = "hello" - this brings nothing
Why is this happening?
The column number has data type int, so:
WHERE number = "2"
is equivalent to:
WHERE number = 2
because MySql implicitly converts "2" to 2, so to match number's data type.
Because the conversion succeeds to convert the string to the number it represents, you say that it works.
But here:
WHERE number = "hello"
the attempt to make an int out of "hello" results in 0, so it is equivalent to:
WHERE number = 0
and you see that this this brings all the zeros.
Now in the case of id, if its data type is varchar then here:
WHERE id = "hello"
no conversion is done and because there is no id with the value "hello" this brings nothing.
If the id's data type is int then again it is equivalent to:
WHERE id = 0
and I guess there is no id with value 0 so this brings nothing.
All this is explained here: Type Conversion in Expression Evaluation
You are mixing data types. MySQL does silent conversion of strings to numbers, converting the leading digits to a number. So '2' is converted to 2. As would 2million.
If there are no leading digits, then the value is converted to 0.
Moral? Don't mix data types. Use the appropriate data type for comparisons.

SQL ignoring string in where condition

Why if I run a query with gibberish at the end of the where condition it incorrectly returns a result.
Example:
SELECT * FROM contractor_table WHERE contractorID = '97sd'
I am getting the row with the ID 97, when I should get no result.
What is happening here is that you are comparing the contractorID column, which is integer or some other numeric type, against a string literal 97sd. This doesn't make any sense, so MySQL, based on its casting rules, attempts to first cast 97sd to an integer. Consider the result of the following query:
SELECT CAST('97sd' AS unsigned);
In fact, this outputs just 97, the integer. So, in practice this means that the "gibberish" at the end of your string literal, which begins with an integer, will be ignored by MySQL.
But best practice here is to always compare columns against the correct literal types. So use the following version always, for best results:
SELECT * FROM contractor_table WHERE contractorID = 97;
This happends when you have column type int or other numeric if you convert it into varchar than it will retun no output

MySQL In clause not giving the right result

In a MySQL table i have a field, containing this value for a given record : "1908,2315,2316"
Here is my sql Query :
SELECT * FROM mytable WHERE 2316 IN (myfield)
I got 0 results!
I tried this :
SELECT * FROM mytable WHERE 2315 IN (myfield)
Still 0 results
And then i tried this :
SELECT * FROM mytable WHERE 1908 IN (myfield)
Surprisingly i obtained the record when searching with 1908! What should i do to also obtain the record when searching with 2315 and 2316 ? What am i missing ?
Thanks
You appear to be storing comma delimited values in a field. This is bad, bad, bad. You should be using a junction table, with one row per value.
But, sometimes you are stuck with data in a particular structure. If so, MySQL provides the find_in_set() functions.
SELECT *
FROM mytable
WHERE find_in_set(2316, myfield) > 0;
You can't use IN() over comma separated list of no.s its better to normalize your structure first for now you can use find_in_set to find results matching with comma separated string
SELECT * FROM mytable WHERE find_in_set('1908',myfield) > 0
This question has been asked and answered before, but I don't want to hunt for it; this question should be closed as a duplicate. But, to answer your question:
The commas in the string, the column value, are just characters. Those are part of the string. They aren't seen as "separators" between values in the SQL text. The way SQL sees it, the column contains a single value, not a "list" of values.
So, in your query, the IN (field) is equivalent to an equals comparison. It's equivalent to comparing to a string. For example:
... WHERE 2316 = '1908,2315,2316'
And those aren't equal, so the row isn't returned. The "surprisingly" finding of a match, in the case of:
... WHERE 1908 IN ('1908,2315,2316')
that's explained because that string is being evaluated in a numeric context. That is, the comparison returns true, because all of these also true:
... WHERE 1908 = '1908,2315,2316' + 0
... WHERE 1908 = '1908xyz' + 0
... WHERE 1908 = '1907qrs' + 1
(When evaluated in a numeric context, a string gets converted to numeric. It just happens that the string evaluates to a numeric value that equals the integer value it's being comparing to.)
You may be able to make use of the MySQL FIND_IN_SET function. For example:
... WHERE FIND_IN_SET(2316,'1908,2315,2316')
But, please seriously reconsider the design of storing comma separated list. I recommend Bill Karwin's "SQL Antipatterns" book...
http://www.amazon.com/SQL-Antipatterns-Programming-Pragmatic-Programmers/dp/1934356557
In mysql IN clause is utilized as
SELECT * FROM mytable WHERE column_name IN (set_of_values) ;
Mention column name instead of values
Please try
SELECT * FROM mytable WHERE LOCATE(CONCAT (',', 2316 ','), CONCAT (',',myfield,',' ) ) <>0

MySQL SUM() always returns a 0 value

I have amounts stored as a varchar in my table.
When attempting to sum them it always returns 0.00.
Below is an example using only one record from the db.
SELECT col1, SUM(CAST(col2 AS DECIMAL(20,2))) derived1
FROM table
WHERE col3 = 'FIT'
AND col1 = '6211195'
GROUP BY col1
This returns one row with a 0 value.
By removing the SUM and CAST from the query, I can see that it is pulling the value as it should, but I can not sum or cast it, adding either of those breaks it and returns 0 again.
I have also tried converting the field to a decimal type and it just zeros all the values.
EDIT:
Ughh, I just ran a REGEX query to detect anything that isnt an alphanumeric value or a decimal point. It appears that there are non ascii characters in the field that I cant see, messing with the type casting. Will continue to update as I learn more.
Show some sample values of col2. A string that really is a number is treated as a number. Hence '1' will become 1.
However, if the string does not start with a digit, '-', or '+' (after leading spaces), then it will be 0 (in most cases). So 'A1' will be 0. And so on. As will '$100'.
The lesson is: If a column contains numbers, store them as numbers. Really simple.