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
Related
I have a task related to MYSQL where I am required to gather a particular set of values from a horizontal table and insert it into another table. The values in the table are like Index_0, AngleValue_0, Index_1, AngleValue_1,.....Index_11, AngleValue_11. I have to identify a set by the Index value, grab all the values of that set (for example if the Index is identified as Index_0, then grab Index_0 and AngleValue_0), and insert it into another table.
I have created a stored procedure for this, but it works for the first time and then stops updating.
Here is how my stored procedure looks.
DELIMITER //
drop procedure if exists updaterearseattorquereapir//
create procedure updaterearseattorquereapir(in ind int, in kanbn varchar(50), in rp_bdg int, in tl_bdg int, in qc_bdg int)
BEGIN
set #cnt = 0;
WHILE #cnt <= 11 DO
set #i = 0;
DROP TEMPORARY TABLE IF EXISTS indexcheck_Temp;
set #sql_check = CONCAT('CREATE TEMPORARY TABLE indexcheck_Temp
Select ',concat('Index_',#cnt),' as a From rearseattorque where ',concat('Index_',#cnt),'=', ind,' and Kanban = ',"#kanbn");
prepare stmt from #sql_check;
Execute stmt;
deallocate prepare stmt;
select #i := a from indexcheck_Temp;
if ind = #i
then
DROP TEMPORARY TABLE IF EXISTS rearseattorquerepair_Temp;
set #sql1 = CONCAT('
CREATE TEMPORARY TABLE rearseattorquerepair_Temp
SELECT Kanban, ',rp_bdg,', ',tl_bdg,', ',qc_bdg,', DTRowUpdate, ',concat('AngleValue_',#cnt),', ',concat('AngleStatus_',#cnt),', ',
concat('Index_',#cnt),', ',concat('Name_', #cnt),', ',concat('Operator_',#cnt),', ',concat('PSETNumber_',#cnt),', ',
concat('TighteningID_',#cnt),', ',concat('TighteningStatus_',#cnt),', ',concat('TimeStamp_',#cnt),', ',
concat('TorqueValue_',#cnt),', ',concat('TorqueStatus_',#cnt),
' FROM rearseattorque where ',concat('Index_',#cnt),'=', ind,' and Kanban = ',"#kanbn");
prepare stmt from #sql1;
EXECUTE stmt;
deallocate prepare stmt;
Insert into rearseattorquerepair(Kanban
,RepairOperatorBadge
, TeamLeaderBadge
, QCBadge
, DTRowUpdate
, Bolt_AngleValue
, Bolt_AngleStatus
, Bolt_Index
, Bolt_Name
, Bolt_Operator
, Bolt_PSETNumber
, Bolt_TighteningID
, Bolt_TighteningStatus
, Bolt_TimeStamp
, Bolt_TorqueValue
, Bolt_TorqueStatus
)
Select * from rearseattorquerepair_Temp;
else
select 'data not found';
END if;
SET #cnt = #cnt + 1;
END WHILE;
END//
DELIMITER ;
I have table users, with fields: id, name, age, gender, country, city, comment. Fields maybe null. For example:
cursor.execute('select * from users where id = 12')
cursor.fetchone()
(12, 'alex', 33, 'male', None, None, None)
How I can get back from query only not null fields?
This query must return me just
(12, 'alex', 33, 'male')
I can easily do it with a programming language, but I losing my resources by getting redundant info from tables and it also forced me to add redundant code.
So, you need to get customized table-oriented result with columns dynamically generated with SQL ANSI.
You will need a kit with two procedures:
Check if a column has only NULL values or not.
Gather all columns with NO NULL values.
With these two procedures you can check each column of a table strcuture and get an output withtout columns with NULL values.
The first procedure is generic:
set delimiter //
create procedure check_field_null(col varchar(64),
schemaname varchar(255),
tablename varchar(255),
out QN int)
BEGIN
SET #sSQL = concat('SELECT #N := COUNT(*) FROM ', schemaname, '.', tablename , ' WHERE (', col, ' <=> NULL);');
prepare stm from #sSQL;
execute stm;
set QN =#N;
deallocate prepare stm;
END
//
The second procedure means that you have the above mentioned method to identify NOT NULLS columns and put all to search.
set delimiter //
create procedure cur_cs_customer(inout allcols varchar(255), in my_schema_name varchar(64), in my_table_name varchar(64))
BEGIN
DECLARE Count_Null int default 0;
DECLARE initial INT DEFAULT 0;
DECLARE MYCOL char(64);
DECLARE ch_done INT DEFAULT 0;
DECLARE cs_cur1 CURSOR
FOR SELECT C.COLUMN_NAME
FROM information_schema.COLUMNS C
WHERE C.TABLE_SCHEMA = my_schema_name
AND C.TABLE_NAME = my_table_name;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET ch_done = true;
open cs_cur1;
read_cs_cur1:
LOOP
FETCH cs_cur1 INTO MYCOL;
IF (ch_done ) THEN
LEAVE read_cs_cur1;
END IF;
IF NOT isnull(MYCOL) THEN
call check_field_null(MYCOL,
my_schema_name,
my_table_name,
Count_Null);
if Count_Null = 0 then
/* Only it inlcudes fields with not null values */
set initial = initial + 1;
if initial = 1 then
SET allcols = MYCOL;
else
SET allcols = concat( cast(allcols as char(255)), ',', MYCOL);
end if;
end if;
END IF;
END LOOP read_cs_cur1;
close cs_cur1;
select allcols;
END
//
After, you may run as follows:
set delimiter ;
call cur_cs_customer(#my_args, 'schema_name', 'users');
select #my_args;
set #stm = concat('SELECT ', #my_args, ' FROM users;');
PREPARE stmt1 FROM #stm;
execute stmt1;
deallocate prepare stmt1;
Further explanation may be checked in an article here! .
I hope this tip may help you.
I have a procedure call that updates a set of data and then returns the data set for my to render. Everything works fine except that, I cant do both operations at once. If I do the update, then the procedure, won´t return any value and vice versa. I have seen some answers suggesting to use temptables but I could not find how to retrieve the dataset. I appreciate any help even if it comes to improving my query.
CREATE DEFINER=`root`#`localhost` PROCEDURE `prueba`(IN `idUsuario` INT)
MODIFIES SQL DATA
BEGIN
DECLARE flag INT DEFAULT FALSE;
DECLARE done INT DEFAULT FALSE;
DECLARE idNotificacion INT DEFAULT 0;
DECLARE cont INT DEFAULT 0;
DECLARE resultset CURSOR FOR SELECT id FROM notificaciones WHERE involvedA_idUsuario=idUsuario AND active=1;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN resultset;
SET #query = "SELECT * FROM notificaciones n WHERE n.id IN('null'";
the_loop: LOOP
FETCH resultset INTO idNotificacion;
IF done THEN
LEAVE the_loop;
END IF;
SET cont = cont + 1;
SET flag = TRUE;
SET #query = CONCAT(#query," , " ,idNotificacion);
UPDATE notificaciones SET active=0 WHERE id=idNotificacion;
END LOOP the_loop;
CLOSE resultset;
IF flag THEN
SET #query = CONCAT(#query, ")");
PREPARE stmt FROM #query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END IF;
END
Do you really need a cursor? An option like this, maybe it can be useful:
/* Procedure structure for procedure `prueba` */
/*!50003 DROP PROCEDURE IF EXISTS `prueba` */;
DELIMITER $$
CREATE PROCEDURE `prueba`(`idusuario` INT)
BEGIN
DECLARE `ids` LONGTEXT DEFAULT NULL;
SELECT GROUP_CONCAT(`id`) INTO `ids`
FROM `notificaciones`
WHERE `involveda_idusuario` = `idusuario` AND `active` = 1;
IF (`ids` IS NOT NULL) THEN
SET #`stmt` := CONCAT('UPDATE `notificaciones`
SET `active` = 0
WHERE `id` IN (', `ids`, ')');
PREPARE `exec` FROM #`stmt`;
EXECUTE `exec`;
SET #`stmt` := CONCAT('SELECT `id`, `involveda_idusuario`, `active`
FROM `notificaciones` `n`
WHERE `n`.`id` IS NULL OR `n`.`id` IN (', `ids`, ')');
PREPARE `exec` FROM #`stmt`;
EXECUTE `exec`;
DEALLOCATE PREPARE `exec`;
END IF;
END$$
DELIMITER ;
You must be careful with GROUP_CONCAT and the system variable group_concat_max_len.
SQL Fiddle demo
I have mysql DB with incremental table names (ie. table1, table2, table3, tablen). Somewhere along the way this incrementation has skipped a few and there are gaps. I want to execute a script which will create the table with the correct table name when the loop finds a missing table. So something like..
for i=1 to 100
create table if not exists 'tablei'
***TABLE FIELD DECLARATIONS***
end
Any help much appreciated.
Something like (might need a few MySql syntax twiddles)
Declare #loopvar int
Declare #sql VarChar(8000)
Declare #table_definition VarChar(8000)
Set #table_definition = '(SomeField int null)'
Set #loopvar = 1
While #loopVar < 100
begin
set #sql = 'Create Table if not exists TableName' + Convert(VarChar(3),#loopvar) + #table_definition
exec(#sql)
set #loopvar = #loopvar + 1
end
should do it.
an answer with all the "MySql syntax twiddles":
DELIMITER //
CREATE PROCEDURE missing_tables(n INT)
BEGIN
WHILE n > 0
DO
SET #sql = CONCAT('create table if not exists table', n, ' (value INT)');
PREPARE cmd FROM #sql;
EXECUTE cmd;
DEALLOCATE PREPARE cmd;
SET n = n - 1;
END WHILE;
END; //
DELIMITER ;
CALL missing_tables(5);
I've created a stored procedure:
DELIMITER $$
DROP PROCEDURE IF EXISTS `zero`.`sp_for_insert_into_account_db`$$
CREATE PROCEDURE `zero`.`sp_for_insert_into_account_db` (usr_key char(6),usr_name varchar(15),usr_password varchar(15),OUT output_message INT)
BEGIN
DECLARE no_of_row INT;
SELECT COUNT(*) INTO no_of_row from account_db;
IF no_of_row < 4 THEN
SET #s = CONCAT('insert into account_db (USR_KEY,USR_NAME,USR_PWD) VALUES (',usr_key,usr_name,usr_password,')');
PREPARE stmt FROM #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET output_message=1;
ELSE
SET output_message=0;
END IF;
END$$
DELIMITER ;
I'm calling it with query
call sp_for_insert_into_account_db('a','b','c',#output_ message);
The error is like:
Column count does not match value...
I'm passing 4 arguments...
Why is this error occurring?
I've already checked with this syntax (by default parameter is IN type)
sp_for_insert_into_account_db(IN usr_key char(6),
IN usr_name varchar(15),
IN usr_password varchar(15),
OUT output_message INT)
Problem is also here:
SET #s = CONCAT('insert into account_db (USR_KEY,USR_NAME,USR_PWD) VALUES (',usr_key,usr_name,usr_password,')');
You are trying to insert 3 values and the concatenation returns 1
use this instead:
SET #s = CONCAT('insert into account_db (USR_KEY,USR_NAME,USR_PWD) VALUES (\'',usr_key,'\',\'',usr_name,'\',\'',usr_password,'\')');
Not entirely sure why you're using prepared statements/dynamic sql when you dont need to ?? See the following example which i've cleaned up for you a little:
drop procedure if exists sp_for_insert_into_account_db;
delimiter #
create procedure sp_for_insert_into_account_db
(
in p_usr_key char(6),
in p_usr_name varchar(15),
in p_usr_pwd varchar(15),
out p_output_message tinyint unsigned
)
begin
declare v_no_of_row int unsigned default 0;
set p_output_message=0;
select count(*) into v_no_of_row from account_db;
if v_no_of_row < 4 then
insert into account_db(usr_key, usr_name, usr_pwd) values (p_usr_key, p_usr_name, p_usr_pwd);
set p_output_message = 1;
end if;
end#
delimiter ;
call sp_for_insert_into_account_db (...);
EDIT
are you a COBOL PROGRAMMER FROM THE 1970'S AND IS THAT WHY YOU HAVE TO USE CAPS ?