I am using a stored procedure with a prepared statment to find rows from a string of email addresses.
It looks like this:
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `get_users_by_email`(IN emailString VARCHAR(100))
BEGIN
SET #sql = CONCAT('SELECT user_id FROM users WHERE user_email IN (',emailString,')');
PREPARE qry FROM #sql;
EXECUTE qry;
END
I then call the procedure using:
CALL get_users_by_email('me#email.com,you#email.com, etc..');
But, I receive this error:
Unknown column in 'me#email.com' in 'where clause'
The query works if I use it to find numeric values such as:
CALL get_users_by_email('123,456');
Any ideas why it would be erroring on alphanumeric values? The query works fine when it is pulled out of the prepared statement and procedure.
You need string delimiters around your email address:
SET #sql = CONCAT('SELECT user_id FROM users WHERE user_email IN (''',emailString,''')');
If you are using a string, you need to pass it with quotes:
SET #sql = CONCAT('SELECT user_id FROM users WHERE user_email IN (''',emailString,''')');
I used extra quotes to escape. The numbers work since numbers are not interpreted as object names if they aren't delimited.
Related
I am experiencing some trouble when I pass date-like strings to a input parameter of a stored procedure.
The table I try to modify has following columns:
create table localdevid.product_a(
INDX int PRIMARY KEY NOT NULL AUTO_INCREMENT,
ProdID int unsigned,
Assigned tinyint,
TesterID varchar(8),
tAss datetime);
Now I try to create a stored procedure:
use localdevid;
drop procedure if exists AssignNewDevID;
DELIMITER $$
use localdevid$$
CREATE PROCEDURE AssignNewDevID(in TableName varchar(255), in TesterName varchar(8), out DevID bigint(20))
BEGIN
#declare rightnow datetime;
set #t1=CONCAT("select SensorID into #localID from localdevid.",TableName," where ISNULL(Assigned) and INDX>1 order by INDX asc limit 1 for update");
prepare statement1 from #t1;
execute statement1;
deallocate prepare statement1;
set DevID=#localID;
set #t2=CONCAT("update localdevid.",TableName," set Assigned=4 where SensorID=",DevID);
prepare statement2 from #t2;
execute statement2;
deallocate prepare statement2;
set #t3=CONCAT("update localdevid.",TableName," set TesterID=",TesterName," where SensorID=",DevID);
prepare statement3 from #t3;
execute statement3;
deallocate prepare statement3;
commit;
END $$
DELIMITER ;
There were several issues, therefore I splitted it into the three statements to see where my problems might come from. I surely will later get it back into one statement back later on.
If I call the function the failure message changes:
call AssignNewDevID("product_a",'tester3',#id);
The script runs to statement2, this is executed successfully.
Statement3 drops Error Code 1054: "Unknown column 'tester3' in Field list.
I cannot understand why the parameter is interpreted as a field name.
It gets even stranger, if I pass a string as TesterName, which can be interpreted as a date or time.
In example, the TesterName are usually MAC-IDs, so the string is i.e. "00:08:01" (I transfer only the last 3 bytes of the MAC).
If I call it like this:
call AssignNewDevID("htpa32x32d",'00:08:01',#id);
I get error code: 1064: You have an error in your SQL syntax; check the manual...
What I am doing wrong here? Why can I concat TableName and DevID but not TesterName?
I don't see any difference here to the the other parameters.
Furthermore, I was not able to pass the current datetime to tAss. I did try the following:
declare rightnow datetime;
declare mydate varchar(20);
select DATE_FORMAT(now(),"%d.%m.%y") as mydate;
...
set #t4=CONCAT("update localdevid.",TableName," set tAss=",mydate," where SensorID=",DevID);
How can I pass basically NOW() to tAss?
OK, got it. Since I pass a string in TesterName I do need of course mark it between 'xxx' in this case.
So it works by
set #t3=CONCAT("update localdevid.",TableName," set TesterID='",TesterName,"' where SensorID=",DevID);
Same applies for the timestamp:
set #mydate=DATE_FORMAT(now(),"%d.%m.%y %h:%i:%S");
set #t4=CONCAT("update localdevid.",TableName," set tAss='",#mydate,"' where SensorID=",DevID);
DELIMITER $$
CREATE PROCEDURE prepared_return_value(IN columnName varchar(20), IN tableName varchar(20), IN rowIndex varchar(10))
BEGIN
SET #columnName = '';
SET #columnName = CONCAT('Select distinct ',columnName ,' from ', tableName, ' LIMIT ', rowIndex, ',', '1', ';');
-- SELECT #columnName;
PREPARE stmt FROM #columnName;
EXECUTE stmt;
END $$
DELIMITER ;
So, i'm trying to dynamic pivoting a table using prepared statements to return the unique value of the rows and use it to alter a table.
But for that, i need to be able to store the returned row value, so i can use it in another prepared statement as the column name.
Apparently i can't store the value from the execution of prepared statement neither from a procedure.
I've tried lot of things by know, but none seemed to work, só... any hint on that?
The Varoable is session bound, so you can be used after the procedure has run .
SELECT #columnName
direct after the running outside if the procedure.
another option s to use an OUT Parameter besides the IN
Ypu would run
CALL return_value(columnName, tableName, rowIndex, #columnName)
And the crete procedure wil look like
CREATE PROCEDURE prepared_return_value(IN columnName varchar(20), IN tableName varchar(20), IN rowIndex varchar(10), OUT myoutput TEXT
Still what you are doing is highly problematic as it could be used for sql injection, so you should white list colu,nname an Tablename
Am I able to write a stored procedure with a parameter, which is the mysql query and the stored procedure returns the column names of the query?
For example I call the procedure:
call selector('select * from users')
And the procedure returns the column names.
It would be easy with information.schema but if I have a more complicated query and alias in it?
Found a solution
CREATE DEFINER=`admin`#`localhost` PROCEDURE `selector`(
IN `sql_query` VARCHAR(50))
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGiN
drop table if exists tmp111;
SET #sql = concat('create table tmp111 as ', sql_query);
PREPARE stmt FROM #sql;
Execute stmt;
describe tmp111;
END
Then call it like:
call selector('select name as n, email as e from users')
I have a stored procedure with two varchar input parameters and one int output parameter. The idea is that I pass in the table name and an unique string, check the table to see if that string already exists in the table and return the id of that record if it does. Currently the sproc looks like:
BEGIN
SET #getID = CONCAT('SELECT `id` as id_Out FROM ',tablename_In,' WHERE `formSecret`=',formSecret_In);
PREPARE stmt FROM #getID;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
If I pass in a number, everything works and I get a record id. If I pass in a full alpha-numeric string (which is what the unique ID's are), it fails, telling me that the column does not exist.
For example: call sp_GetRecordID(123456,'tbl_justsaythanks',#id_Out); will return a record ID just like I want.
But if I try a real id from the table that's a string...
`call sp_GetRecordID('fc66d9a82ba717e0931462370e64baff','tbl_justsaythanks',#id_Out);`
I get "Error Code: 1054. Unknown column 'fc66d9a82ba717e0931462370e64baff' in 'where clause'"
Since I'm new to MySQL, I'm not sure where I'm off on this - I suspect it has something to do with the application of tick marks in the query to designate the columns but I can't see it at the moment.
So, any help in pointing me the right way would be greatly appreciated.
Try a stored procedure like the following:
DROP PROCEDURE IF EXISTS `sp_GetRecordID`;
DELIMITER //
CREATE PROCEDURE `sp_GetRecordID`(
`tablename_In` VARCHAR(64),
`formSecret_In` VARCHAR(32),
OUT `id_Out` BIGINT UNSIGNED
)
BEGIN
SET #`query` := CONCAT('SELECT `id` INTO #`id_Out`
FROM ', `tablename_In` ,'
WHERE `formSecret` = \'', `formSecret_In`, '\'');
PREPARE `stmt` FROM #`query`;
EXECUTE `stmt`;
SET `id_Out` := #`id_Out`,
#`query` := NULL;
DEALLOCATE PREPARE `stmt`;
END//
DELIMITER ;
See db-fiddle example.
I've written a stored procedure function to get a name from a table. The trouble is that I want the table name to be passed in as a parameter (there are several different tables I need to use this function with):
DELIMITER $$
CREATE DEFINER=`root`#`localhost` FUNCTION `getName`(tableName VARCHAR(50), myId INT(11)) RETURNS VARCHAR(50)
begin
DECLARE myName VARCHAR(50);
SELECT
'name' INTO myName
FROM
tableName
WHERE
id=myId;
RETURN myName;
end
This method has an error because it uses the variable name "tableName" instead of the actual value of the variable.
I can work around this problem in a procedure by using a CONCAT like this:
SET #GetName = CONCAT("
SELECT
'name'
FROM
",tableName,"
WHERE
id=",myId,";
");
PREPARE stmt FROM #GetName;
EXECUTE stmt;
...but, when I try to do this in a function I get a message saying:
Dynamic SQL is not allowed in stored function or trigger
I tried to use a procedure instead, but I couldn't get it to just return a value, like a function does.
So, can anyone see a way to get around this problem. It seems incredibly basic really.
If you want to buld a SQL statement using identifiers, then you need to use prepared statements; but prepared statements cannot be used in functions. So, you can create a stored procedure with OUT parameter -
CREATE PROCEDURE getName
(IN tableName VARCHAR(50), IN myId INT(11), OUT myName VARCHAR(50))
BEGIN
SET #GetName =
CONCAT('SELECT name INTO #var1 FROM ', tableName, ' WHERE id=', myId);
PREPARE stmt FROM #GetName;
EXECUTE stmt;
SET myName = #var1;
END
Using example -
SET #tableName = 'tbl';
SET #myId = 1005;
SET #name = NULL;
CALL getName(#tableName, #myId, #name);
SELECT #name;