I am trying to run a procedure that takes a parameter 'table' for the query, and result as the output parameter. However, it shows as undeclared variable: result
I have doubled checked that no spelling mistake but still have no idea how it happened. Would someone please provide some help or guidance
CREATE DEFINER=`root`#`localhost` PROCEDURE `Function`(IN table varchar(10), OUT result varchar (10))
BEGIN
SET #q = CONCAT ('
Select `field` from `',table,'` into result limit 1;');
PREPARE stmt from #q;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
Try:
...
-- SET #q = CONCAT ('Select `field` from `',table,'` into result limit 1;');
SET #`q` := CONCAT('SELECT `der`.`field`
FROM (SELECT `field` FROM `', `table`, '` LIMIT 1) `der`,
(SELECT #`result` := NULL) `init`
INTO #`result`;');
PREPARE `stmt` from #`q`;
EXECUTE `stmt`;
SET `result` := #`result`;
DEALLOCATE PREPARE `stmt`;
...
It is important to indicate the difference between 9.4. User-Defined Variables and routine parameters 13.1.15. CREATE PROCEDURE and CREATE FUNCTION Syntax, are different variables.
SQL Fiddle demo
Related
I have created a function in phpMyAdmin as shown in this screenshot.
When I try to use it like this:
Select DMax ("id","customers")
I get error #1305 saying that uTable does not exist. This is probably some basic syntax issue, as uTable in the sql statement is taken literally and not seen as a parameter. So how do I make it work?
You can't use parameters to a procedure for column or table names. Instead, you need to prepare a statement using those values and execute that. For example:
BEGIN
DECLARE uValue INT(11);
SET #sql = CONCAT('SELECT MAX(', uField, ') INTO uValue FROM ', uTable);
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
RETURN uValue;
END
Note that you cannot use dynamic SQL in a function, so you would need to convert this into a stored procedure with uValue an OUT parameter i.e.
CREATE PROCEDURE DMax(
IN uField VARCHAR(100),
IN uTable VARCHAR(100),
OUT uValue <appropriate type>
)
BEGIN
DECLARE uValue INT(11);
SET #sql = CONCAT('SELECT MAX(', uField, ') INTO uValue FROM ', uTable);
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
Then you would need to call the procedure, something like
CALL DMax('table1', 'column1', #DMax)
and you can then
SELECT #DMax
(yes, this is a monumental pain)
I want to create a function that will create a unique random id. The parameters will simply be min (the minimum number), max (the maximum number), and tablename (the name of the table to check to see if the id produced by the rand() function already exists).
I have discovered through other posts that you can't pass table names into functions, because functions can't execute dynamic SQL, but you can pass them into stored procedures. I have found numerous examples on StackOverflow of how to pass table names into stored procedures, and they all boil down to using prepared statements.
I have created a stored procedure as shown below:
DELIMITER $$
CREATE DEFINER=`user`#`localhost` PROCEDURE `rand_id`(IN `min` INT, IN `max` INT, IN `tablename` VARCHAR(20) CHARSET utf8, OUT `uid` INT)
BEGIN
DECLARE count_id int;
SET count_id = 1;
SET #s = CONCAT('COUNT(`id`) INTO count_id FROM `', tablename, '` WHERE `id` = ', uid);
WHILE count_id > 0 DO
SET uid = FLOOR(rand() * max + min);
PREPARE stmt from #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END WHILE;
END$$
DELIMITER ;
Whenever I run the following code:
CALL rand_id(1000000000, 9999999999, 'test', #id);
SELECT #id;
I get this error:
#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'NULL' at line 1
I'm at a loss for what's wrong. I saw somewhere that you can't use user variables inside a stored procedure, but that seems to be incorrect because there are a lot of examples on StackOverflow where the correct solutions do just that.
Sorry for my low level of MySQL understanding. I'm sure my code is fraught with syntax errors and poor design. I appreciate any help I can get. I researched this for quite a while and tried many things but to no avail. The above portion of code is the closest I've been able to get, and yields the least errors, but it's still not working.
Thank you.
EDIT: As per the second example in #Barmar's answer, I changed my code to look like this:
BEGIN
DECLARE count_id int;
SET count_id = 1;
SET #s = CONCAT('SELECT COUNT(`id`) INTO count_id FROM `', tablename, '` WHERE `id` = ?');
PREPARE stmt from #s;
WHILE count_id > 0 DO
SET #uid = FLOOR(rand() * max + min);
EXECUTE stmt USING #uid;
END WHILE;
DEALLOCATE PREPARE stmt;
SET uid = #uid;
END
It seems to have fixed my initial problem but now I get this error:
#1327 - Undeclared variable: count_id
EDIT: Here is my code changed to fit #slaakso's answer, and add in what #Barmar said about using #count_id:
DELIMITER $$
CREATE DEFINER=`mjrinker`#`localhost` PROCEDURE `rand_id`(IN `min` BIGINT, IN `max` BIGINT, IN `tablename` VARCHAR(128) CHARSET utf8, OUT `uid` BIGINT)
BEGIN
SET #count_id = 1;
SET uid = 0;
SET #s = CONCAT('SELECT COUNT(`id`) INTO #count_id FROM `', tablename, '` WHERE `id` = ?');
PREPARE stmt from #s;
WHILE #count_id > 0 DO
SET #uid = FLOOR(rand() * max + min);
EXECUTE stmt USING #uid;
END WHILE;
DEALLOCATE PREPARE stmt;
SET uid = #uid;
END$$
DELIMITER ;
You need to assign #s after you assign the uid variable.
You're also missing the SELECT keyword in your query.
SET #count_id = 1
WHILE #count_id > 0 DO
SET uid = FLOOR(rand() * max + min);
SET #s = CONCAT('SELECT COUNT(`id`) INTO #count_id FROM `', tablename, '` WHERE `id` = ', uid);
PREPARE stmt from #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END WHILE;
But you should actually just prepare the statement once, using a placeholder, which you fill in when using EXECUTE.
SET #count_id = 1
SET #s = CONCAT('SELECT COUNT(`id`) INTO #count_id FROM `', tablename, '` WHERE `id` = ?');
PREPARE stmt from #s;
WHILE #count_id > 0 DO
SET #uid = FLOOR(rand() * max + min);
EXECUTE stmt USING #uid;
END WHILE;
DEALLOCATE PREPARE stmt;
SET uid = #uid;
Note that the parameters to EXECUTE have to be user variables, that's why I changed uid to #uid there. Then we set the output parameter at the end of the loop.
You also need to use a user variable for INTO #count_id.
First of all it is highly unusual to use random numbers as ID's for tables. Mabye you should consider using AUTO_INCREMENT columns.
If you really want to use random numbers, couple of fixes for the code:
You should use value for uid for the first time you run the query
(without it it will be NULL, therefore the error).
You are missing SELECT in your dynamic query
The "INTO count_id" syntax will not work as count_id is not visible inside the dynamic SQL (use #var variable instead)
Your min and max values are declared as INT's, but your passed parameters exceed the INT range (-2147483648 - 2147483647)
CREATE DEFINER=`root`#`localhost` PROCEDURE `get_plan_images`(
_list varchar(2000)
)
BEGIN
SET #LIST=_list;
set #sql = concat('SELECT plan_id, plan_image
FROM ibuild.plan_images
where plan_id in (', #LIST , ')');
PREPARE q FROM #sql;
execute q;
END
What does concat do here? And what does this statement means PREPARE q FROM #sql;
CONCAT simply merges two or more pieces of text together. In the example above, #LIST contains a list of ids passed in to the procedure. After the CONCAT, #sql becomes a string that contains something like:SELECT plan_id, plan_image FROM ibuild.plan_images where plan_id in (1,2,3)
See here: MySQL CONCAT Function
The PREPARE statement just gives the statement a name so you can execute it. See here: MySQL PREPARE
I'm trying to create a stored procedure which will take a comma separated list as a value and push that into a select statement with an IN clause. I'm aware of find_in_set which works, but does have a performance overhead. I've also trialled a prepare statement but the problem remains.
My Example:
My parameter is sp1 and contains the value: 'valuex, valuey, valuez'.
BEGIN
set #sql = concat('select * from tablename WHERE assignedTo in (', sp1, ')');
PREPARE q FROM #sql;
execute q;
END
As expected, this throws an error since SQL will treat the value as column names. My question is how do I achieve: 'valuex','valuey','valuez' FROM 'valuex, valuey, valuez'?
You can use replace function:
BEGIN
set #sql = concat("select * from tablename WHERE assignedTo in ('", replace(sp1,",","','"), "')");
PREPARE q FROM #sql;
execute q;
END
An option is:
DELIMITER $$
DROP PROCEDURE IF EXISTS `sp_test`$$
CREATE PROCEDURE `sp_test`(`sp1` VARCHAR(50))
BEGIN
SET `sp1` := REPLACE(REPLACE(`sp1`, ', ', ','), ',', '\',\'');
SET #`query` := CONCAT('SELECT `column0`, `column1` FROM `tablename` WHERE `assignedTo` IN (\'', `sp1`, '\');');
PREPARE `stmt` FROM #`query`;
EXECUTE `stmt`;
DEALLOCATE PREPARE `stmt`;
END$$
DELIMITER ;
CALL `sp_test`('valuex, valuey, valuez');
-- SELECT `column0`, `column1` FROM `tablename` WHERE `assignedTo` IN ('valuex','valuey','valuez');
1)Below is the code I'm trying for a procedure where I need to compare the count from two different tables. (tbl1 and tbl2 are in parameters to be passed).
But in the above code the values for 'a' and 'b' are set as statements. I need the resultant value(count) from the two statements to compare in the 'IF' condition.
declare a integer;
declare b integer;
set #a:=concat('select count(*) from ', tbl1);
/*PREPARE stmt1 FROM #a;
execute stmt1;
deallocate PREPARE stmt1;*/
set #b:= concat('select count(*) from ', tbl2);
/*PREPARE stmt2 FROM #b;
execute stmt2;
deallocate PREPARE stmt2;*/
if
#a=#b
then
select 1;
else
select 2;
end if;
2) Actually where I am facing a problem to set 'tbl1' and 'tbl2' as parameters in procedure.
The below code works fine if the table names are given directly, but i need them as parameters.
CREATE PROCEDURE myproc (in tbl1 varchar(50), in tbl2 varchar(50))
declare a integer;
declare b integer;
set #a:=(select count(*) from tbl1);
/*PREPARE stmt1 FROM #a;
execute stmt1;
deallocate PREPARE stmt1;*/
set #b:= (select count(*) from tbl2);
/*PREPARE stmt2 FROM #b;
execute stmt2;
deallocate PREPARE stmt2;*/
if
#a=#b
then
select 1;
else
select 2;
end if;
Hope anyone out there can help me with the solution.
As mentioned in the comments above, it is likely that you shouldn't be doing this at all... but, for what it's worth:
SET #sql := CONCAT('SELECT (
SELECT COUNT(*) FROM `',REPLACE('`','``',tbl1),'`
) = (
SELECT COUNT(*) FROM `',REPLACE('`','``',tbl2),'`
);');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET #sql := NULL;
REPLACE() has been used to try and avoid SQL injection (it's somewhat crude, but it's the best one can do without further information).