Single Quotes in MySQL queries - mysql

If I have a MySQL query like:
SELECT this FROM that WHERE id='10'
and
SELECT this FROM that WHERE id=10
both seem to work correctly.
What is the use of the single speech marks in MySQL queries? When is it correct to use them?

When MySQL performs the query, there is an implicit conversion of the argument.
If id is INT, then '10' is cast to an integer.
If id is VARCHAR or another text type, 10 is cast to string.
In both cases both queries will work (unless you are running in STRICT mode).
From a performance point of view, you have to use the right data type (do not use quotes for integer arguments) - the implicit cast adds overhead and in some cases, it may hurt the performance of index lookups.
From security perspective, it easier to always use quotes and mysql_real_escape_string (in case the argument is not quoted, mysql_real_escape_string won't stop any attack, that do not use quotes, for example 'UNION SELECT password FROM users'. However, better approach is to cast your variable to int, when it's expected to be int, or use prepared statements

If the value is a string, you have to use ' or ".
If the value is a number, like in your example, you have not to use ', but MySQL handles it if you put it around 's.

Assuming that id is a numeric column, what happens is that MySQL casts your parameter to number automatically so data types match before comparing. It works flawlessly unless casting provides unexpected results. E.g., these expressions with match the row with id=10 because all the strings cast to 10:
id='10'
id=' 10'
id='00010'
id='10foo'
The following will not match the row because non-parseable strings cast to 0 and 10<>0:
id='foo10'
id='bar'
When to use each? If you want a string, you need to quote it (there's no other way to type a string and get valid SQL). If you want a number, it must be unquoted (otherwise, you'll get a string that happens to contain a number). Of course, you can always provide numbers as strings and let MySQL do the conversion, but it doesn't really add anything to the query apart from one extra step and possibly incorrect results that go unnoticed.

You should always use them. They can help to stop SQL injection attacks because mysql_real_escape_string isn't enough on its own.
That is assuming you are running a query via PHP.

Related

Why does a query return a result if the field is numeric and the WHERE clause is a string?

I am running a query on a db table which is returning one record when I expect it to return no records.
SELECT yeargroupID FROM tbl_yeargroup WHERE yeargroup='S' AND schoolID=2.
The yeargroup field is a tinyint field. Thefore the WHERE clause is looking the letter 'S' in the numeric field, so should not find anything. Yet it returns the record with the yeargroup = 0, and yeargroupID=17 (the bottom record in the table)
I'm confused as to why it is returning this record and how to avoid it.
Thanks
This logic, as you have pointed out, is comparing a number and a string:
WHERE yeargroup = 'S'
Handling such situations is an important part of most SQL compilers, and it is well documented. The solution is to implicitly convert values to "conforming" types. This is sad. My preference would be for the compiler to generate an error and force the user to use correct types. I find that implicit conversion creates more problems than it solves.
In any case, the rules in this case are pretty simple. The string is converted to an integer. But, how is a string with no digits converted? Well, the rule in MySQL is that the leading digits are converted to a number. And if there are none, the value is 0. So, this turns into:
where yeargroup = 0
You can see the results more clearly if you run:
select 'S', 'S' + 0
Note that most databases would return an error in this case (a type conversion error). But even those would accept the string if it looked like a number, so this would be allowed:
where yeargroup = '5'
What is the proper solution? Never mix types. Do not construct queries by munging constant values. Instead, queries from an application should always be using parameters.

SQL Query giving wrong results

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;

Using REGEX to alter field data in a mysql query

I have two databases, both containing phone numbers. I need to find all instances of duplicate phone numbers, but the formats of database 1 vary wildly from the format of database 2.
I'd like to strip out all non-digit characters and just compare the two 10-digit strings to determine if it's a duplicate, something like:
SELECT b.phone as barPhone, sp.phone as SPPhone FROM bars b JOIN single_platform_bars sp ON sp.phone.REGEX = b.phone.REGEX
Is such a thing even possible in a mysql query? If so, how do I go about accomplishing this?
EDIT: Looks like it is, in fact, a thing you can do! Hooray! The following query returned exactly what I needed:
SELECT b.phone, b.id, sp.phone, sp.id
FROM bars b JOIN single_platform_bars sp ON REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(b.phone,' ',''),'-',''),'(',''),')',''),'.','') = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(sp.phone,' ',''),'-',''),'(',''),')',''),'.','')
MySQL doesn't support returning the "match" of a regular expression. The MySQL REGEXP function returns a 1 or 0, depending on whether an expression matched a regular expression test or not.
You can use the REPLACE function to replace a specific character, and you can nest those. But it would be unwieldy for all "non-digit" characters. If you want to remove spaces, dashes, open and close parens e.g.
REPLACE(REPLACE(REPLACE(REPLACE(sp.phone,' ',''),'-',''),'(',''),')','')
One approach is to create user defined function to return just the digits from a string. But if you don't want to create a user defined function...
This can be done in native MySQL. This approach is a bit unwieldy, but it is workable for strings of "reasonable" length.
SELECT CONCAT(IF(SUBSTR(sp.phone,1,1) REGEXP '^[0-9]$',SUBSTR(sp.phone,1,1),'')
,IF(SUBSTR(sp.phone,2,1) REGEXP '^[0-9]$',SUBSTR(sp.phone,2,1),'')
,IF(SUBSTR(sp.phone,3,1) REGEXP '^[0-9]$',SUBSTR(sp.phone,3,1),'')
,IF(SUBSTR(sp.phone,4,1) REGEXP '^[0-9]$',SUBSTR(sp.phone,4,1),'')
,IF(SUBSTR(sp.phone,5,1) REGEXP '^[0-9]$',SUBSTR(sp.phone,5,1),'')
) AS phone_digits
FROM sp
To unpack that a bit... we extract a single character from the first position in the string, check if it's a digit, if it is a digit, we return the character, otherwise we return an empty string. We repeat this for the second, third, etc. characters in the string. We concatenate all of the returned characters and empty strings back into a single string.
Obviously, the expression above is checking only the first five characters of the string, you would need to extend this, basically adding a line for each position you want to check...
And unwieldy expressions like this can be included in a predicate (in a WHERE clause). (I've just shown it in the SELECT list for convenience.)
MySQL doesn't support such string operations natively. You will either need to use a UDF like this, or else create a stored function that iterates over a string parameter concatenating to its return value every digit that it encounters.

Are numbers seen as strings in sql

This is really simple. Does SQL SERVER 2008 auto convert values to string for you ?
I tried this Select * from Staff Where Division = 5
If I try to insert it, it works too.
If I change 5 for five I get and error though. Invalid column name five.
Division is a NVARCHAR. Shoudn't the 5 be within single quotes ?
Yes, SQL Server does an implicit conversion of int to nvarchar. It can also implicitly convert nvarchar to int (and other numeric types).
For more details: http://msdn.microsoft.com/en-us/library/ms191530.aspx
There are a lot of cases where implicit conversion takes place. For example:
EXEC sp_who2 active;
Shouldn't you need to delimit active as a string? Yes, but the syntax is forgiving.
My suggestion: always delimit data types correctly, and ignore the exceptions. The chances they'll ever get comprehensively fixed are close to nil.

Is there a mySQL equivalent to Postgresql's "Dollar-Quoted String Constants"?

In postgresql, it's possible to use "Dollar-Quoted String Constants" to avoid escaping single quotes in constant strings:
Without Dollar-Quoted String Constants:
'Jeff''s table'
With Dollar-Quoted String Constants:
$$Jeff's table$$
Is there a MySQL equivalent?
On edit: I'm not looking for a way to sanitize inputs, I'm looking for a way to make queries that generate sql easier to read.
No, because it doesn't really work. An attacker just addes a pair of dollar signs to their injection attempt instead.
The correct way to handle this is a system that uses real query parameters, such that the parameter values are never substituted directly into a query string. This is also generally better for performance because the db can do a better job caching the execution plan you don't end up building string dynamically, which tends to be slow in modern languages.
Not having excess concatenation statements greatly improves the readability of the code, as well. Instead of this:
sql = "SELECT * FROM MyTable WHERE Column1=$$" + somevarialbe + "$$ AND Column2=$$" + OtherVariable";
it's just this:
sql = "SELECT * FROM MyTable wHERE Column1=#Value1 AND Column2=#Value2";
Hmm... Okay, I can see some limited utility for this feature now.
Imagine a ticket tracking system with a stored procedure to get open tickets. You might actually hard-code the literal 'open' into the where clause of the query. Not that this would be a good design — ticket status should be in a table with it's own key, so that you'd hardcode the key rather than the text. But it plants the seed for something valid and more-complicated.
You can enclose your string in double quotes instead, e.g.
"Jeff's table"
Note: If this doesn't work, it means that you've got ANSI_QUOTES SQL mode turned on. The MySQL docs say:
If the ANSI_QUOTES SQL mode is enabled, string literals can be quoted only within single quotation marks because a string quoted within double quotation marks is interpreted as an identifier.