MySQL: Passing procedure params to EXECUTE USING statement - mysql

This is MySQL 5.1.
(Note: I realize there are better ways of doing this particular example, this is not my real code)
Here is what I want to do:
The below procedure gets created, but when I CALL it, I get "ERROR 1210 (HY000): Incorrect arguments to EXECUTE"
DELIMITER //
CREATE PROCEDURE get_users_by_state(IN state CHAR(2))
READS SQL DATA
BEGIN
SET #mystate = state;
SET #sql = CONCAT('SELECT * FROM test_table WHERE state = "?"');
PREPARE stmt FROM #sql;
EXECUTE stmt USING #mystate;
END;
//
CALL get_users_by_state('AA')//
ERROR 1210 (HY000): Incorrect arguments to EXECUTE
Is there a way to pass the procedure's parameters to the EXECUTE USING statement?
Here is a version that does indeed work, but irks me:
CREATE PROCEDURE get_users_by_state(IN state CHAR(2))
READS SQL DATA
BEGIN
SET #sql = CONCAT('SELECT * FROM test_table WHERE state = "', state, '"')
PREPARE stmt FROM #sql;
EXECUTE stmt;
END;
//
As a side-question, does MySQL have any facilities for escaping strings, like Postgres' quote_literal() and quote_ident()?
For a point of reference, here's something somewhat equivalent for Postgres:
CREATE OR REPLACE FUNCTION get_info_by_state(character)
RETURNS SETOF ret_type AS
$BODY$
DECLARE
sql text;
BEGIN
sql := 'SELECT uid, some_data FROM test_table WHERE state = ' || quote_literal($1);
RETURN QUERY EXECUTE sql;
END
$BODY$
LANGUAGE 'plpgsql' VOLATILE
Thanks!

I don't think you need double quotes around the parameter holder.
Update Here, lest there be no misunderstanding:
DELIMITER //
CREATE PROCEDURE get_users_by_state(IN state CHAR(2))
READS SQL DATA
BEGIN
SET #mystate = state;
SET #sql = CONCAT('SELECT * FROM test_table WHERE state = ?');
PREPARE stmt FROM #sql;
EXECUTE stmt USING #mystate;
END;
//

Related

How to loop comma separated values into select statement in MySQL stored procedure

I have to send comma separated values into a select statement where it will update values through #sql statement.
I have common table in all Databases I need to update the table column by one update statement in the procedure.
For Example : Input Param will be ('DataBase1','Database2',....., 'Database10')
Below is the sample procedure :
DELIMITER &&
CREATE PROCEDURE update_stmt (IN DBName varchar(100))
BEGIN
Declare DBName = #DB;
**comma seperated values loop and placed into the #DB**
use #DB;
SELECT concat(update #DB.sample SET COL = 0 where ID = \'',ID,'\','; ) as stmt FROM
Test.Sample into #s;
SET #sql = #s
PREPARE stmt from #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END &&
DELIMITER ;
so that update statement will execute in each of the databases.
Here's another approach. I don't try to split the comma-separated string, I use it with FIND_IN_SET() to match schema names in INFORMATION_SCHEMA.TABLES. This filters to schemas in the list that actually exist, and tables that actually exist in that schema.
Then use a cursor to loop over the matching rows, so you don't have to split any strings, which is awkward to do in a stored procedure.
I supposed that you would want to specify the id of the row to update too, so I added that to the procedure parameters.
Also notice the use of quotes when I create #sql. You can concatenate strings, but those must be quote-delimited like any other string literal. Variables must not be inside the quoted string. There's no feature to expand variables inside string literals in MySQL.
DELIMITER &&
CREATE PROCEDURE update_stmt (IN schema_name_list VARCHAR(100), IN in_id INT)
BEGIN
DECLARE done INT DEFAULT false;
DECLARE schema_name VARCHAR(64);
DECLARE cur1 CURSOR FOR
SELECT TABLE_SCHEMA FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'sample' AND FIND_IN_SET(TABLE_SCHEMA, schema_name_list);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = true;
SET #id = in_id;
OPEN cur1;
schema_loop: LOOP
FETCH cur1 INTO schema_name;
IF done THEN
LEAVE schema_loop;
END IF;
SET #sql = CONCAT('UPDATE `', schema_name, '`.sample SET col = 0 WHERE id = ?');
PREPARE stmt FROM #sql;
EXECUTE stmt USING #id;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE cur1;
END &&
DELIMITER ;
Frankly, I hardly ever use stored procedures in MySQL. The procedure language is primitive, and the tasks I see people try to do in stored procedures could be done a lot more easily in virtually every other programming language.

how to define a stored procedures which accepts 2 parameteres

I´m trying to define a stored procedures which accepts 2 parameteres , one would be the table column which has to be equal with the second parameter i will provide.
Code :
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `selectUserByField`(IN _field varchar(150) , IN _value varchar(150))
BEGIN
SET #sql = CONCAT('SELECT * FROM Users WHERE', _field, '=' ,_value);
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
The thing is that i don´t know how to provide the _value param as a string. If i run it like this i get a Mysql 1064 near ´=myEmail´( params where ´userEmail´,´myEmail´). Thanks !
In your below code, you are missing a space after WHERE. It should be like below; give a space after WHERE and in =
SET #sql = CONCAT('SELECT * FROM Users WHERE ', _field, ' = ' ,_value);

how to set value to output parameter in dynamic sql procedure?

I have tried with following code. But it shows error msg like this undeclared variable :nt.
CREATE DEFINER=`root`#`localhost` PROCEDURE `get_ntime`(in tb varchar(50),in d int, out nt varchar(50))
BEGIN
SET #statment = concat('Select ntime into nt from ',tb);
SET #date = CONCAT(' WHERE date = "', d, '"');
SET #statmen = CONCAT(#statment, #date);
PREPARE stmt FROM #statment;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
When used within single quotes nt will not be treated as variable but a literal.
Use local variable to set value into and assign the same to out param after execution.
Example:
DELIMITER //
CREATE DEFINER=`root`#`localhost`
PROCEDURE `get_ntime`( in tb varchar(50), in d int, out nt varchar(50) )
BEGIN
SET #staetment = concat( 'Select ntime into #nTime from ', tb );
-- SET #date = CONCAT( ' WHERE date = "', d, '"' );
SET #date = CONCAT( ' WHERE date = ?' );
SET #statement = CONCAT( #statement, #date );
SET #dt := d;
PREPARE stmt FROM #statement;
-- EXECUTE stmt;
EXECUTE stmt using #dt;
DEALLOCATE PREPARE stmt;
SET nt := #nTime;
END;
//
DELIMITER ;
Prepared statements have session scope, mysql doesn't know you want to use your prepared statement inside stored procedure only. You deallocate the statement immediately, but it doesn't have to be always like that.
Thats why mysql simply disallows using anything that has less scope inside your prepared statement - as is the case with in and out parameters, which have a scope of stored procedure.
As a workaround mentioned in mysql prepare statement manual you can use user variable inside your prepared statement and then SET your out paremeter to that user variable.

what is wrong with MySQL select Store Procedure with dynamic query

i have tried to create MySQL store procedure in that i want to make dynamic query.
code on which i working..
DELIMITER $$
DROP PROCEDURE IF EXISTS `test`.`selectp` $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `selectp`(in a_str_Condition varchar(500))
BEGIN
SET #Query = 'SELECT * from test123';
IF a_str_Condition != ''
THEN
SET #strCondition = CONCAT(' WHERE ? ');
SET #param = a_str_Condition;
ELSE
SET #strCondition = ' Order by aaa desc';
END IF
SET #Query = CONCAT(#Query, #strCondition );
PREPARE stmt FROM #Query;
EXECUTE stmt USING #param;
DEALLOCATE PREPARE stmt;
END $$
DELIMITER ;
here i want pass parameter as aaa = 3 and concat it with mysql query, but it show me error as below.
please let me clear on this store procedure. Any help will be Appreciate.
Trying to answer to your ...what is wrong with MySQL select Store Procedure..?
As others mentioned you have ; missing. That only solves a syntax error.
But you have bigger problems with the way you try to construct your query and EXECUTE it:
You're incorrectly trying to treat all where conditions as one parameter with WHERE ?, instead of parameterizing values like WHERE id = ?
In case you don't pass a condition you can't use USING in EXECUTE. It'll fail.
Now since you don't execute your query multiple times, you pass conditions as a string anyway, and it seems that you're more after flexibility than security, IMHO there is no much sense in using parameters here.
That being said a more succinct version of your SP might look like this
DELIMITER $$
CREATE PROCEDURE selectp(IN _conditions VARCHAR(500), IN _orderby VARCHAR(500))
BEGIN
SET #sql = CONCAT(
'SELECT * FROM test123 WHERE 1 = 1',
COALESCE(CONCAT(' AND ', NULLIF(_conditions, '')), ''),
' ORDER BY ',
COALESCE(NULLIF(_orderby, ''), 'id DESC')
);
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END$$
DELIMITER ;
Usage examples:
-- default parameters either with null or an empty string
CALL selectp('', '');
CALL selectp(NULL, NULL);
-- set where conditions
CALL selectp('id IN(1, 3)', NULL);
-- set order by
CALL selectp(NULL, 'col1 DESC, col2');
Here is SQLFiddle demo
You've lost a semicolon after END IF.
And that's what mysql is pointed you to - it always shows you the expression part it couldn't parse. So always look at the code right before the one from the error description.
There should be a semi column after the END IF statement:
ELSE
SET #strCondition = ' Order by aaa desc';
END IF; //add semicolumn here
Before this line:
SET #Query = CONCAT(#Query, #strCondition );

use a variable for table name in mysql sproc

I'm trying to pass a table name into my mysql stored procedure to use this sproc to select off of different tables but it's not working...
this is what I"m trying:
CREATE PROCEDURE `usp_SelectFromTables`(
IN TableName varchar(100)
)
BEGIN
SELECT * FROM #TableName;
END
I've also tried it w/o the # sign and that just tells me that TableName doesn't exist...which I know :)
SET #cname:='jello';
SET #vname:='dwb';
SET #sql_text = concat('select concept_id,concept_name,',#vname,' from enc2.concept a JOIN enc2.ratings b USING(concept_id) where concept_name like (''%',#cname,'%'') and 3 is not null order by 3 asc');
PREPARE stmt FROM #sql_text;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
An extra bit that caused me problems.
I wanted to set the table name and field dynamically in a query as #kyle asked, but I also wanted to store the result of that query into a variable #a within the query.
Instead of putting the variable #a into the concat literally, you need to include it as part of the string text.
delimiter //
CREATE PROCEDURE removeProcessed(table_name VARCHAR(255), keyField VARCHAR(255), maxId INT, num_rows INT)
BEGIN
SET #table_name = table_name;
SET #keyField = keyField;
SET #maxId = maxId;
SET #num_rows = num_rows;
SET #sql_text1 = concat('SELECT MIN(',#keyField,') INTO #a FROM ',#table_name);
PREPARE stmt1 FROM #sql_text1;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
loop_label: LOOP
SET #sql_text2 = concat('SELECT ',#keyField,' INTO #z FROM ',#table_name,' WHERE ',#keyField,' >= ',#a,' ORDER BY ',#keyField,' LIMIT ',#num_rows,',1');
PREPARE stmt2 FROM #sql_text2;
EXECUTE stmt2;
DEALLOCATE PREPARE stmt2;
...Additional looping code...
END LOOP;
END
//
delimiter ;
So in #sql_text1 assign the result of the query to #a within the string using:
') INTO #a FROM '
Then in #sql_text2 use #a as an actual variable:
,' WHERE ',#keyField,' >= ',#a,' ORDER BY '
It depends on the DBMS, but the notation usually requires Dynamic SQL, and runs into the problem that the return values from the function depend on the inputs when it is executed. This gives the system conniptions. As a general rule (and therefore probably subject to exceptions), DBMS do not allow you to use placeholders (parameters) for structural elements of a query such as table names or column names; they only allow you to specify values such as column values.
Some DBMS do have stored procedure support that will allow you to build up an SQL string and then work with that, using 'prepare' or 'execute immediate' or similar operations. Note, however, that you are suddenly vulnerable to SQL injection attacks - someone who can execute your procedure is then able to control, in part, what SQL gets executed.