Script does not work when place the function inside the WHERE clause - mysql

I am trying to create a stored procedure, which receives a string and places it in the WHERE clause after processing it.
I created the function and when I call it from the body of a SQL statement, it returns the correct values:
('J1245',j3456','j1098')
However, when I call the function inside the WHERE clause as shown below, SQL does not show any records.
(Altcode in dbo.myfunction(#codes))
When I hard code it inside the WHERE clause, the SQL statement shows records.
(Altcode IN ('J1245',j3456','j1098'))

I suspect the function is returning a string (VARCHAR). If we reference the function anywhere in a SQL statement, the value returned by the function serves as a scalar value. The contents of the value don't change that. Any punctuation, single quotes, backticks, parens, commas, keywords, identifiers, et al. that happen to appear in the string are just part of the value.
The parser sees the return from the function simply as a single value, not as part of the SQL text.
It's not possible (in a single statement) to make the value returned by a function "become" part of the SQL text to be parsed.
As an example, in the WHERE clause, if we write:
WHERE altcode IN ( myfunc() )
The function myfunc is going to be evaluated, and the return will be single value, of a certain datatype, for example maybe a VARCHAR. Any parens or commas within the value are not interpreted as part of the SQL text. Those are just characters that are within the value.
It's as if we wrote SQL like this:
WHERE altcode IN ( ? )
And supplied a single value in place of the question mark placeholder. The SQL parser isn't seeing a list of values, it's not seeing any SQL to be executed. All the statement is seeing is a value.
It matters not one whit that we might supply a value that looks like SQL text, for example:
WHERE altcode IN ( '(SELECT code FROM all_altcodes)' )
That would be equivalent to writing
WHERE altcode = '(SELECT code FROM all_altcodes)'
And that is going to look for an exact match of altcode to the string literal.
Seems like there's a single quote missing in the value returned by the function, but maybe that's a typo in the question.
To get the string value returned by the function included as part of the SQL text, we would need to use dynamic SQL.
We would have to first call the function, and return the string. And then do some string concatenation to come up with another string that contains the SQL statement we want to execute, and then execute that string as a SQL statement.
So that would be two separate statement executions... one to get the function evaluated; and a second statement that is dynamically constructed, as a string, incorporating the value returned by the function.
(The question is tagged "MySQL", but I suspect this question is actually regarding SQL Server (given the reference to dbo. ?)
Preparing and executing dynamic SQL is similar in MySQL and SQL Server, but there are differences in the syntax.

Related

MYSQL Best way to parametrize variable values for IN clause in stored procedure

I have to write a stored procedure where I want to set values for a variable called colorId using IN operator, the parameter can be a list of integer ids or no ids. I am wondering what should be the type of variable in the stored procedure?
where color_id IN (1,2,3,4);
Thanks for the help!
If you send a string like '1,2,3,4' as a single parameter, the query will run as if you had done this:
where color_id IN ('1,2,3,4');
The way MySQL does implicit type casts to integer, this converts the value to an integer using only the leading digits, and ignores everything after the first comma. So it will really run as if you had done this:
where color_id IN (1);
There is no way to "remove" the quotes. The point of query parameters is that they are not combined with the query until after the SQL parsing is done. Therefore the parameter is fixed as a single string value in that expression. You can't convert a parameter into a list of discrete values, because that would change the syntax of the query.
So you must pass multiple parameters, one for each value in your list. Like this:
...where color_id IN (?, ?, ?, ?);
And use some function in your client application to split the string into multiple parameters and then pass them not as a single string value, but as multiple integer values.
Some people try to use tricks like using MySQL's FIND_IN_SET() function, but I don't recommend this, because it cannot be optimized with any index.
You tagged this question stored-procedures from which I infer that you are trying to write a procedures that accepts a string of comma-separated integers and use it in an IN() predicate. This is more inconvenient to do in a stored procedure than in any other programming language, because MySQL's stored procedure language doesn't support arrays or good functions for splitting strings or counting elements. It can be done with enough effort, but the code is awful and you will quickly wish you were using any other language.
Your can pass parameter value like this - '1,2,3,4' and FIND_IN_SET function will be able to search in the provided string:
SELECT *
FROM colors
WHERE FIND_IN_SET(color_id, param); # param --> '1,2,3,4'

How to escape a whole sql string instead of escaping each argument?

I use https://github.com/mysqljs/mysql.git library.
I have a mysql db query architecture in which I can not modify the SQL query file one by one to escape each argument for there are too many files, but all the SQL queries will call the query method of a same base mysql instance, so I wonder if I can escape the eventual SQL string in the base mysql query method.
I want to escape the whole SQL string like
select * from tableA where name = 'foo'bar
to
select * from tableA where name = 'foo\'bar'
with some function like mysql_escape("select * from tableA where name = 'foo'bar'") instead of doing this using preparing queries or concating escaped strings.
There isn't a way to do this that wont result in a really inefficient function or some bad hack. Just use parameterized queries, Its basically what they are there for. If you cant use those you use concat strings.
Running mysql_escape on a whole query will require the function to know what characters are part of your query and what characters are part of the input values. You could write some kind of stupid regex to try pull the values from the query and then escape them but its just a bad idea.

DECODE Function in SQL

I am trying to insert the following query and I get syntax errors. Can you please help me with the below query:
INSERT INTO ABCTABLE (COLUMN1) values ('DECODE(MDSE_CD,NULL,'0000000000000000',LPAD(TO_NUMBER(MDSE_CD,'16',' '))');
Since you haven't really said anything other than "this query doesn't work, fix it", I have to take a stab in the dark what you want. From the query you have, I'm therefore guessing you want the value of the column to be DECODE(MDSE_CD,NULL,'0000000000000000',LPAD(TO_NUMBER(MDSE_CD,'16',' '))
In which case, you have to escape the single quotes within your string literal. Do this by doubling up the quotes:
INSERT INTO ABCTABLE (COLUMN1)
VALUES ('DECODE(MDSE_CD,NULL,''0000000000000000'',LPAD(TO_NUMBER(MDSE_CD,''16'','' ''))')
Try properly escaping the inner single quotes
INSERT INTO ABCTABLE (COLUMN1)
VALUES ('**DECODE**(MDSE_CD,NULL,''0000000000000000'',**LPAD**(TO_NUMBER(MDSE_CD,''16'','' ''))');
The problem is the use of quote marks. If we tried to break up your query it would look like this:
INSERT INTO ABCTABLE
(COLUMN1)
values
(
'DECODE(MDSE_CD,NULL,'
0000000000000000
',LPAD(TO_NUMBER(MDSE_CD,'
16
','
'))'
);
...which clearly makes no sense.
You might want to think about how to escape a quote mark inside a string.
Sql Server:
DECOD function in Sql Server can be replaced with CASE construct
LPAD function in Sql Server has not a direct correspondence but you can pad your string using string manage function REPLACE (replicate a character a number of specified times)
My Sql:
DECOD function in MySql can be replaced with CASE construct
LPAD function in MySql is existent
What do you want to store... a string literal 'DECODE(MDSE...))', or did you want to call a function to derive a value?
To store a string literal containing single quotes, you need to "escape" each single quote within the string with an extra single quote, e.g.
O'Hare Int'l ==> 'O''Hare Int''l'
The DECODE function is Oracle specific. That expression will need to be rewritten using different functions in both MySQL and SQL Server.

mysql query parameter string array

I am trying to pass a parameter to a query, rather than write copious text I have narrowed it down to this simple explanation.
The frament I am trying to insert into is
where pkw_0.keyword in (:kwd)
I have used a String[] to construct a string of the form vals="'AVal','BVal'" which I pass to the query using setParameter("kwd",vals); The query returns zero results. However if I construct the query by hand and use the mysql console the query returns 1 result which is expected.
So I am assuming that either a single string is incorrect for the parameter or there is some conditioning of the values that I need to do prior to passing them via the setParameter call.
Each parameter can only represent a single literal value. You will need to create multiple placeholders in your prepared statement (one for each value) and then provide each value to MySQL as a separate parameter.

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.