First Stored procedure with parameter in MySQL - mysql

this is my query in MySQL on table tbl_T367
mysql> SELECT
oID,
xName
FROM
`tbl_T367`
WHERE
oID IN ('2')
AND xName IN ('T367');
+-----+-------+
| oID | xName |
+-----+-------+
| 2 | T367 |
+-----+-------+
1 row in set
i need create mysql stored procedure for the same output like above
my code below
DELIMITER $$
DROP PROCEDURE IF EXISTS my_sqrt$$
CREATE PROCEDURE my_sqrt(xName char(4), oID INT (11))
BEGIN
DECLARE xNamenew CHAR (4);
DECLARE IDnew INT (11);
SET #xNamenew = xName;
SET #IDnew = oID;
SET #s = CONCAT('SELECT * FROM tbl_', #xNamenew,
' WHERE oID IN (' + #IDnew + ') AND xName IN (' + #xNamenew + ')');
PREPARE stmt FROM #s;
EXECUTE stmt;
END
DELIMITER ;
but the result
Procedure execution failed
1146 - Table 'tbl_T3672' doesn't exist
why was the value of the second variable added to the table name?
mysql> SELECT
oID,
xName
FROM
`tbl_T3672`
WHERE
oID IN ('2')
AND xName IN ('T367');
1146 - Table 'tbl_t3672' doesn't exist
mysql>
how to do resolve this?
update
SET #s = CONCAT('SELECT * FROM tbl_', #xNamenew,
' WHERE oID IN (' , #IDnew , ') AND xName IN (' , #xNamenew ,')');
PREPARE stmt FROM #s;
EXECUTE stmt;
Procedure execution failed
1054 - Unknown column 'T367' in 'where clause'

Some issues (I haven't reviewed the original procedure thoroughly):
MySQL doesn't have a string concatenation operator. + is the addition operator thus your strings will be cast to numbers and added.
You're inflicting yourself SQL injection. Literal strings in SQL must be single-quoted, but that isn't something you need to take care yourself.
You're invoking the procedure from itself.
You are missing the ending delimiter.
DROP PROCEDURE IF EXISTS my_sqrt;
DELIMITER $$
CREATE PROCEDURE my_sqrt(xName char(4), oID INT (11))
BEGIN
DECLARE xNamenew CHAR (4);
DECLARE IDnew INT (11);
SET #xNamenew = xName;
SET #IDnew = oID;
SET #s = CONCAT('SELECT * FROM tbl_', #xNamenew,
' WHERE oID = ? AND xName = ?');
PREPARE stmt FROM #s;
EXECUTE stmt USING #IDnew, #xNamenew;
END
$$
DELIMITER ;
CALL my_sqrt('T367', '2');

Alvaro's answer shows you how to use parameterized queries to reduce the chances of injection. This procedure below is just for educational purposes to show you the difference between what you had and how close you were to getting it right.
There are some variables you didn't need. You can take the input and create a concatenated string. Since you would send '2', it may not be concatenated with the single quotes. So, you have to include single quotes in the concatenation like shown below.
delimiter $$
drop procedure if exists my_sqrt$$
create procedure my_sqrt(xName varchar(10), oID varchar(10))
begin
set #s = concat(
"select * from tbl_", xName,
" where oID in ('", oID, "')",
" and xName in ('", xName, "')"
);
prepare stmt from #s;
execute stmt;
end$$
delimiter ;
Again, this is for educational purposes. Use Alvaro's method.

Related

Getting unknown column id on stored procedure call

I have following stored procedure to reset auto increments for a table that gets many inserts and deletes.
DROP PROCEDURE IF EXISTS reset_autoincrement;
DELIMITER $$;
CREATE PROCEDURE reset_autoincrement(IN tableName VARCHAR(250))
BEGIN
SELECT #max := MAX(`id`) + 1 + concat(' FROM ', tableName );
set #alter_statement = concat('ALTER TABLE ', tableName ,' AUTO_INCREMENT = ', #max);
PREPARE stmt FROM #alter_statement;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END $$;
and call it with
call reset_autoincrement('queueIn');
and get
23:18:25 call reset_autoincrement('queueIn') Error Code: 1054. Unknown column 'id' in 'field list' 0,042 sec
A little excerpt of the table's columns:
id bigint(19) UN AI PK
created_at timestamp
updated_at timestamp
I have two questions.
Obviously ... why do I get this error - the column clearly is there.
The id column gets incremented to a million within 6 hours, there are many insert/delete operations throughout the day. Is there an even better way to reset auto_increments in mysql?
Because you are trying to make it a dynamic query (the below line) and as it stands currently it's a mixture of dynamic and normal query.
SELECT #max := MAX(`id`) + 1 + concat(' FROM ', tableName );
You should make it a dynamic query fully
SET #sql = "SELECT MAX(`id`) + 1 FROM " + tableName;
You would need to hold the variable from first dynamic query and use it in ALTER statement. Something like below
DELIMITER $$;
CREATE PROCEDURE reset_autoincrement(IN tableName VARCHAR(250))
BEGIN
SET #var1 = 0;
SET #sql = "SELECT MAX(`id`) + 1 INTO #var1 FROM " + tableName;
PREPARE stmt FROM #sql;
EXECUTE stmt;
SET #ID = #var1;
set #alter_statement = concat('ALTER TABLE ', tableName ,' AUTO_INCREMENT = ', #ID);
PREPARE stmt FROM #alter_statement;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END $$;

unable to store return value from execute statement in mysql

Using MySQL, I am trying to create a stored proc to retrieve the maximum value of a varchar column in any given table. I would want to increment the value of the column by 1 and then use it to store the other fields. I do not want the column to be int and auto increment.
the stored proc i have for now is
DELIMITER $$
use gounicartdb$$
DROP PROCEDURE IF EXISTS sp_getUpdatedIDFromTable$$
CREATE PROCEDURE sp_getUpdatedIDFromTable(
IN tableName varchar(50),
IN columnName varchar(50),
IN incrementValue int/*,
OUT updatedID varchar(10)*/
)
BEGIN
SET #newID = "abc";
SET #cmdString = concat("Select max(", columnName, ") from ", tableName);
PREPARE stmt FROM #cmdString;
SELECT #newID = EXECUTE stmt;
END$$
DELIMITER ;
When compiling I see no errors, but when executing the procedure the following error occurs.
14:50:48 Call sp_getUpdatedIDFromTable("user", "SNo", 1) Error Code: 1054. Unknown column 'EXECUTE' in 'field list' 0.000 sec
Please help.
You can replace in your procedure
SET #cmdString = concat("Select max(", columnName, ") into #newID from ", tableName);
PREPARE stmt FROM #cmdString;
EXECUTE stmt;
SELECT #newID;

Assigning a SQL result to variable from prepared statement in MySQL

I am creating a stored procedure in MySQL and need to assign the results of a SQL query to a variable. The problem is that in order to create the SELECT statement, I have to use the CONCAT() function because I am passing in parameters.
Well it appears you can't use variables within the CONCAT function. Any ideas on how I can achieve this? The procedure I am trying to write is below:
DELIMITER //
CREATE PROCEDURE `my_proc` (IN tbl VARCHAR(20), IN col VARCHAR(20), IN id INT)
BEGIN
DECLARE #myval VARCHAR(100);
SET #t1 =CONCAT('SELECT ',col,' FROM ',tbl,' INTO #myval WHERE id = ',id );
PREPARE stmt1 FROM #t1;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
END //
Executing this gives me a SQL syntax error.
The problem is the following line:
...
-- SET #t1 = CONCAT('SELECT ',col,' FROM ',tbl,' INTO #myval WHERE id = ',id );
SET #t1 = CONCAT('SELECT ', col, ' INTO #myval FROM ', tbl, ' WHERE id = ', id);
...
Check the documentation: 13.2.9.1. SELECT ... INTO Syntax.
Here is a SQL Fiddle with an example.
It is important to indicate the difference between 9.4. User-Defined Variables (#t1 and #myval) and 13.6.4.1. Local Variable Syntax DECLARE (as could be: myval and t1), are different variables, therefore, it is not necessary to declare:
-- DECLARE #myval VARCHAR (100);

MySQL stored procedure Error - Create table from param names

I'm trying the following but I'm getting the following error:
ERROR 1054 (42S22): Unknown column 'f' in 'where clause'
I'm seriously confused because f is a parameter of createtableTest...
CREATE PROCEDURE createtableTest
(
tname2 varchar(20),
f varchar(20)
)
BEGIN
DROP TABLE IF EXISTS tname2;
CREATE TABLE tname2 as SELECT * FROM data WHERE group_name like f;
END;
Since f is contains the value, a dynamic sql is needed so we can concatenate it with the original query,
DELIMITER $$
CREATE PROCEDURE createtableTest(IN tname2 varchar(20),IN f varchar(20))
BEGIN
DROP TABLE IF EXISTS tname2;
SET #sql = CONCAT('CREATE TABLE tname2 as SELECT * FROM data WHERE group_name like ''%',f,'%''');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END $$
DELIMITER ;
for example, the value of f is hello, the concatenated string will produce
CREATE TABLE tname2 as SELECT * FROM data WHERE group_name like '%hello%'
UPDATE
MySQL Prepared Statements
Aside from concatenation, you can also parameterized the value which is the best way, ex
DELIMITER $$
CREATE PROCEDURE createtableTest(IN tname2 varchar(20),IN f varchar(20))
BEGIN
DROP TABLE IF EXISTS tname2;
SET #sql = CONCAT('CREATE TABLE tname2 as SELECT * FROM data WHERE group_name like ?');
PREPARE stmt FROM #sql;
SET #val = CONCAT('%', f, '%');
EXECUTE stmt USING #val;
DEALLOCATE PREPARE stmt;
END $$
DELIMITER ;

MySql, split a string and insert into table

I have two inputs for my stored procedure. One is the 'RoledID' and second one is the 'MenuIDs'. 'MenusIDs' is a list of comma separated menus ids that need to be inserted with RoledID. RoleId is just an INT and we need to put this RoledID against each MenuID. My table 'RolesMenus' contains two columns one for MenuID and one for RoleID.
Now I need to split MenuIDs and insert each MenuID with RoleID.
How can I write a stored procedure for it?
You can build one INSERT query (because statement allows to insert multiple records) and run it with prepared statements, e.g. -
SET #MenuIDs = '1,2,3';
SET #RoledID = 100;
SET #values = REPLACE(#MenuIDs, ',', CONCAT(', ', #RoledID, '),('));
SET #values = CONCAT('(', #values, ', ', #RoledID, ')'); -- This produces a string like this -> (1, 100),(2, 100),(3, 100)
SET #insert = CONCAT('INSERT INTO RolesMenus VALUES', #values); -- Build INSERT statement like this -> INSERT INTO RolesMenus VALUES(1, 100),(2, 100),(3, 100)
-- Execute INSERT statement
PREPARE stmt FROM #insert;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
As you see, it can be done without stored procedure.
Give this a go. It may need some tweaking if the MenuIDs string does not conform to 'menuId,menuId,menuId'.
Also I do not know what data type the menuId column is in your target table (INT?) so you may have to put some numeric checking in too (in case '1,2,3,banana,4,5' is passed in as the MenuIds input parameter).
DELIMITER $$
DROP PROCEDURE IF EXISTS `insert_role_menuids`$$
CREATE PROCEDURE `insert_role_menuids`(IN RoleID INT,IN MenuIDs varchar(500))
BEGIN
declare idx,prev_idx int;
declare v_id varchar(10);
set idx := locate(',',MenuIDs,1);
set prev_idx := 1;
WHILE idx > 0 DO
set v_id := substr(MenuIDs,prev_idx,idx-prev_idx);
insert into RolesMenus (RoleId,MenuId) values (RoleID,v_id);
set prev_idx := idx+1;
set idx := locate(',',MenuIDs,prev_idx);
END WHILE;
set v_id := substr(MenuIDs,prev_idx);
insert into RolesMenus (RoleId,MenuId) values (RoleID,v_id);
END$$
DELIMITER ;
for this solution, you must create a table with the name split_table, it can have a id(autoincrement) if you need it and must have a column where to store the value (I call it valor)
DELIMITER $$
USE `dbaname`$$
DROP PROCEDURE IF EXISTS `Split`$$
CREATE DEFINER=`root`#`localhost` PROCEDURE `Split`(
IN cadena VARCHAR(8000),
IN delimitador VARCHAR(10)
)
BEGIN
TRUNCATE split_table;
SET #posicion = 1;
SET #ldel = LENGTH(delimitador);
SET #valor = SUBSTRING_INDEX(cadena, delimitador, 1);
WHILE #valor <> '' AND #posicion > 0 DO
SET #valor = SUBSTRING_INDEX(cadena, delimitador, 1);
INSERT INTO split_table(valor) VALUES (#valor);
SET #posicion = POSITION(delimitador IN cadena);
SET #largo = LENGTH(cadena);
IF #largo >= #posicion THEN
SET cadena = SUBSTR(cadena, #posicion + #ldel, #largo - #posicion);
SET #valor = SUBSTRING_INDEX(cadena, delimitador, 1);
ELSE
SET #posicion = 0;
END IF;
END WHILE;
END$$
DELIMITER ;
First create procedure
CREATE DEFINER=`root`#`localhost` PROCEDURE `split_str_save_to_tmp_table`(
IN _str TEXT,
IN _table_name VARCHAR(80)
)
BEGIN
#DROP FIRST OLD TABLE
SET #q = CONCAT('DROP TEMPORARY TABLE IF EXISTS ', _table_name);
PREPARE st FROM #q;
EXECUTE st;
#CREATE TABLE
SET #q = CONCAT('CREATE TEMPORARY TABLE ', _table_name, '(id INT UNSIGNED NOT NULL PRIMARY KEY (id) )' );
PREPARE st FROM #q;
EXECUTE st;
SET #ids = REPLACE(_str, ',', '),(');
SET #ids = CONCAT('(', #ids, ')');
#INSERT INTO TABLE
SET #q = CONCAT('INSERT INTO ' , _table_name ,' VALUES');
SET #q = CONCAT(#q, #ids);
PREPARE st FROM #q;
EXECUTE st;
DEALLOCATE PREPARE st;
END
Then call
call split_str_save_to_tmp_table('1,2,3,4,5', 'tmp_split_product');
SELECT * FROM tmp_split_product
AFAIK MySQL does not have a function to split strings. Here is the MySQL manual for string related functions. In the comments section should be some information about workarounds for splitting string with substring-functions but not really usable:
MySQL manual