MySQL: comparison of integer value and string field with index - mysql

Table a_table has index on string_column.
I have a query:
SELECT * FROM a_table WHERE string_column = 10;
I used EXPLAIN to find that no indexes are used.
Why? Could you help me with MySQL documentation link?
Updated: Sandbox (SQL Fiddle)

The essential point is that the index cannot be used if the database has to do a conversion on the table-side of the comparison.
Besides that, the DB always coverts Strings -> Numbers because this is the deterministic way (otherwise 1 could be converted to '01', '001' as mentioned in the comments).
So, if we compare the two cases that seem to confuse you:
-- index is used
EXPLAIN SELECT * FROM a_table WHERE int_column = '1';
The DB converts the string '1' to the number 1 and then executes the query. It finally has int on both sides so it can use the index.
-- index is NOT used. WTF?
EXPLAIN SELECT * FROM a_table WHERE str_column = 1;
Again, it converts the string to numbers. However, this time it has to convert the data stored in the table. In fact, you are performing a search like cast(str_column as int) = 1. That means, you are not searching on the indexed data anymore, the DB cannot use the index.
Please have a look at this for further details:
http://use-the-index-luke.com/sql/where-clause/obfuscation/numeric-strings
http://use-the-index-luke.com/sql/where-clause/functions/case-insensitive-search

Related

Is it a problem to compare a string to an INT column in MySQL

if I have an int column of id, is it bad for performance to have a query written like
SELECT * FROM products where id = '1'?
Or does MySQL handle this the same as
SELECT * FROM products where id = 1
In general: don't do this. Compare values against a literal of the correct datatype, so you don't have to worry about what is going on under the hood. If you have an integer column, then:
SELECT * FROM products WHERE id = 1
In this specific situation: when asked to compare a number to a string, MySQL converts the string to a number. So there would be (almost) no performance penalty if you were to use:
SELECT * FROM products WHERE id = '1'
The literal string is converted once, and then checked against every value in the column.
On the other hand, consider a string column, say str, and a predicate like:
WHERE str = 1
Now it goes the other way around. MySQL needs to convert all string values in str to numbers before they can be compared. The expression becomes inefficient - it cannot take advantage of an index (it is non-SARGable). If there are many rows, the performance impact may be important.
So, back to the initial recommendation: know your datatypes; use the proper literal when comparing.
If performance is your concern, numbers are smaller than text in this example--1 is faster than "1".
HOWEVER... if you're looking for performance never start with SELECT *
The cases:
WHERE int_col = 2 -- Good
WHERE int_col = "2" -- Good; the "2" is turned into INT at start of query
WHERE int_col = "2foo" -- Probably not what you wanted
WHERE char_col = 2 -- Slow; `char_col` is turned into INT for each row
WHERE char_col = "2" -- Good

MySQL returns all rows when field=0 from SECOND Select query

This case is similar to: S.O Question; mySQL returns all rows when field=0, and the Accepted answer was a very simple trick, to souround the ZERO with single quotes
FROM:
SELECT * FROM table WHERE email=0
TO:
SELECT * FROM table WHERE email='0'
However, my case is slightly different in that my Query is something like:
SELECT * FROM table WHERE email=(
SELECT my_column_value FROM myTable WHERE my_column_value=0 AND user_id =15 LIMIT 1 )
Which in a sense, becomes like simply saying: SELECT * FROM table WHERE email=0, but now with a Second Query.
PLEASE NOTE: It is a MUST that I use the SECOND QUERY.
When I tried: SELECT * FROM table WHERE email='( SELECT my_column_value FROM myTable WHERE my_column_value=0 LIMIT 1 )' (Notice the Single Quotes on the second query)
MySql SCREAMED Errors near '(.
How can this be achieved
Any Suggestion is highly honored
EDIT1: For a visual perspective of the Query
See the STEN_TB here: http://snag.gy/Rq8dq.jpg
Now, the main aim is to get the sten_h where rawscore_h = 0;
The CURRENT QUERY as a whole.
SELECT sten_h
FROM sten_tb
WHERE rawscore_h = (
SELECT `for_print_stens_rowscore`
FROM `for_print_stens_tb`
WHERE `for_print_stens_student_id` =3
AND `for_print_stens_factor_name` = 'Factor H' )
The result of the Second Query can be any number including ZERO.
Any number from >=1 Works and returns a single corresponding value from sten_h. Only =0 does not Work, it returns all rows
That's the issue.
CORRECT ANSWER OR SOLUTION FOR THIS
Just in case someone ends up in this paradox, the Accepted answer has it all.
SEE STEN_TB: http://snag.gy/Rq8dq.jpg
SEE The desired Query result here: http://snag.gy/wa4yA.jpg
I believe your issue is with implicit datatype conversions. You can make those datatype conversions explicit, to gain control.
(The "trick" with wrapping a literal 0 in single quotes, that makes the literal a string literal, rather than a numeric.)
In the more general case, you can use a CAST or CONVERT function to explicitly specify a datatype conversion. You can use an expression in place of a column name, wherever you need to...
For example, to get the value returned by my_column_value to match the datatype of the email column, assuming email is character type, something like:
... email = (SELECT CONVERT(my_column_value,CHAR(255)) FROM myTable WHERE ...
or, to get the a literal integer value to be a string value:
... FROM myTable WHERE my_column_value = CONVERT(0,CHAR(30)) ...
If email and my_column_value are just indicating true or false then they should almost certainly be both BIT NOT NULL or other two-value type that your schema uses for booleans. (Your ORM may use a particular one.) Casting is frequently a hack made necessary by a poor design.
If it should be a particular user then you shouldn't use LIMIT because tables are unordered and that doesn't return a particular user. Explain in your question what your query is supposed to return including exactly what you mean by "15th".
(Having all those similar columns is bad design: rawscore_a, sten_a, rawscore_b, sten_b,... . Use a table with two columns: rawscore, sten.)

What is the best way to select a rows by comparing ENUM and SET columns

I mean if any table consist of ENUM or SET typed column and I will make the query like:
SELECT * FROM `tabname` WHERE `enum_field` = 'case1' OR `enum_field` = 'case2'
Will it be efficient?
Or the engine will convert number stored in enum_field to string, compare it to the pattern (in my example - case1 and case2)?
What is the most efficient way to use such columns?
Thanks!
The syntax you provided above is correct.
The database engine will convert the strings in your query into numeric indexes, which will be used when searching the table.
According to the mysql documentation, you can also query directly by numeric index, but this can sometimes have unexpected results, particularly if any of your enum string values are numeric.
So assuming "case1" has index 1, and "case2" has index 2, you could rewrite your query like this:
SELECT * FROM `tabname` WHERE `enum_field` = 1 OR `enum_field` = 2

Is OK to enclosing all values in SQL statement with single quotes?

Is it ok to enclose all values in SQL statement with single quotes ? For example:
This is simple table called books:
id | title
1 | Some book name
2 | Second book name
Is it OK to write statement like this:
SELECT * FROM books WHERE id = '1'
I've tested that query in SQL server 2008 and MySQL 5 and it works good, but I am curious is there any performance issue, because ID field is acctualy integer.
And second question is it OK to write statement like this:
SELECT * FROM books WHERE id = N'1'
N prefix is used in SQL server for UTF-8 fields, but I've tested that in SQL server and MySQL and both worked OK. I don't know if SQLite support N prefix, because I didn't test that.
The reason why I am asking this is because I am building database class that will work with popular SQL databases (SQL Server, MySQL, SQLite and maybe MS Access), so when performing selecting, inserting or updating data I don't have to worry about field datatype. I can always enclose value with N'Some value', but I am curious is this correct and is there any performance issues?
In SQL Server, you can get an implicit conversion when doing this. Sometimes it won't affect the plan or have noticeable impacts on performance, but sometimes it might. They are generally deemed to be bad. Inspect the plans for these two queries against AdventureWorks2012:
SELECT * FROM Sales.SalesOrderHeader WHERE SalesOrderID = 43659;
SELECT * FROM Sales.SalesOrderHeader WHERE SalesOrderID = '43659';
The latter contains a CONVERT_IMPLICIT (hover over the tooltip for the CI seek).
You also want to be very careful about switching between VARCHAR and NVARCHAR - in some cases this can be dreadful for performance, depending on the underlying data type and whether the literal has the N prefix.
Long story short: don't do this. Write your ORM thingy so that it understands the different data types and handles them appropriately.
SELECT ... WHERE int_type = '123' is fine to do. SQL will convert '123' to an integer once and be done with it.
However, SELECT ... WHERE char_type = 123 is no okay, because SQL will have to convert every cell into an integer. See that char_type = '0123' and char_type = '00123' will also match. So it has to do more work.
Thanks to #MartinSmith for pointing out a resource for precedence in casting: http://msdn.microsoft.com/en-us/library/ms190309.aspx
Here's an example bug in MySQL that says the implicit typecast from quoted string of digits to an integer causes a severe performance issue: http://bugs.mysql.com/bug.php?id=43319
It's best to not quote numbers.
SQL Server and MySQL both perform implicit type conversions, but it doesn't make it a good practice; use the appropriate native type when you can. This will let you avoid trying to spot the difference between:
SELECT * FROM books WHERE id = '1'
and
SELECT * FROM books WHERE id = 'l'
and
SELECT * FROM books WHERE id = 'O'
and
SELECT * FROM books WHERE id = '0'
(that's a one, a lower case L, a capital 'o', and a zero respectively)

MySQL: how can i convert a string '1,2,3,4' to a (1,2,3,4) so i'll be able to use it in a 'where X in ()' statement

I ran a query that resulted in the string '1,2,3,4'.
How can I run a second query that treats that string as a list of numbers. So I'll be able to do:
select * from tbl where name not in (1,2,3,4)
I would like an answer in pure MySQL.
Well first of all, this usually means that your database structure is not good; you should normalize your database.
However, you can do what you want, with the FIND_IN_SET function:
SELECT * FROM tbl WHERE NOT FIND_IN_SET(name, '1,2,3,4')
Use FIND_IN_SET:
select * from tbl where FIND_IN_SET(name, '1,2,3,4') = 0
Like the other answer, I would also recommend normalizing your database if at all possible. This query could be slow as it will require a scan of the table. Even if there is an index on name this query won't be able to use it efficiently.