Logic condition to avoid duplicate entries in stored procedure - mysql

I have the following stored procedure . I'm trying to insert the users from the table usuaris, whose admin variable is equal to 1, into the table that the stored procedure creates with the name( nombre varchar(50)) that is passed as a parameter.
When the procedure is called, it duplicates the user 'mary' with id 4. I've tried a couple of ways to implement the logic condition in order to avoid the duplication, but still, I'm missing something and I can't get the desired result. In the code below, the logic condition before the insertion is the last thing I've tried. Any ideas?
Thanks.
CREATE DEFINER=`root`#`localhost` PROCEDURE `createNewtable`(nombre varchar(50))
BEGIN
/*variable declaration*/
declare centinela int ;
declare id1 int ;
declare nom1 varchar(50);
declare admin1 enum('0','1') ;
declare cadena varchar(100); /*string to concatenate table creation and insertion*/
/*cursor declaration*/
declare cursor1 cursor for select * from users.usuaris where admin = '1' ;
declare continue handler for not found set #centinela = 1 ;
/*create the table with the name that's passed as parameter*/
set #cadena=concat("create table ",nombre,
"(
id2 int not null primary key,
nom2 varchar(50),
admin2 enum ('0','1')
)" );
prepare stmt from #cadena ;
execute stmt ;
deallocate prepare stmt;
/* loop that fetches the data from the table usuaris and
inserts them into the newly created table. */
set #centinela = 0 ;
open cursor1 ;
bucle: loop
fetch cursor1 into id1,nom1,admin1 ;
if ( centinela = 1 ) then
leave bucle ;
end if ;
/*logic condition to avoid entry duplication */
if not exists (select * from users.usuaris where admin='1' and id=#id1) then
set #cadena=concat("insert into ",nombre," values( ",id1,",'",nom1,"','",admin1,"')");
end if;
select #cadena;
prepare stmt from #cadena;
execute stmt ;
deallocate prepare stmt;
end loop bucle;
close cursor1;
END
Here is the single-table database of users :
create database if not exists `users` ;
use `users` ;
create table usuaris(
id int not null auto_increment primary key ,
nom varchar(50),
admin enum ('0','1')
);
insert into usuaris(id,nom,admin)
values
(1,'jose','1'),
(2,'maria','0'),
(3,'frank','1'),
(4,'mary','1'),
(5,'godfrey','0') ;

Also it has to duplicate jose. The reason of duplication - if the IF statement isn't TRUE then you don't set the new #cadena variable BUT anyway execute PREVIOUS #cadena statement. You should move execution into the IF statement also:
if not exists (select * from users.usuaris where admin='1' and id=#id1) then
set #cadena=concat("insert into ",nombre," values( ",id1,",'",nom1,"','",admin1,"')");
select #cadena;
prepare stmt from #cadena;
execute stmt ;
deallocate prepare stmt;
end if;
Also in SQL you should always try to avoid loops if it possible and use SQL statements instead.
You can replace your loop with one SQL statement:
INSERET INTO NEW_TABLE_NAME_HERE
SELECT id1,nom1,admin1
FROM users.usuaris where admin<>'1'
Further more you can use SELECT INTO statement syntax to automatically create new table without CREATE TABLE statement:
SELECT id1 as id2,
nom1 as nom2,
admin1 as admin2
INTO NEW_TABLE_NAME_HERE
FROM users.usuaris where admin<>'1'

Change ur below code to my new code and try-
Existing Code
if not exists (select * from users.usuaris where admin='1' and id=#id1) then
set #cadena=concat("insert into ",nombre," values( ",id1,",'",nom1,"','",admin1,"')");
end if;
select #cadena;
prepare stmt from #cadena;
execute stmt ;
deallocate prepare stmt;
New Code-
SET #cnt=SELECT count(*) FROM users.usuaris WHERE admin='1' AND id=#id1
IF #cnt>0 THEN
SET #cadena=CONCAT("insert into ",nombre," values( ",id1,",'",nom1,"','",admin1,"')");
prepare stmt from #cadena;
execute stmt ;
deallocate prepare stmt;
end if;

Related

The execution of a statement seems to alter the variable defining the request

I am facing a problem that I can't solve or explain.
My goal:
Create a stored procedure for MySQL to execute action like explode/split function in order to use the result of this procedure as a table.
My Procedure :
DROP PROCEDURE IF EXISTS `P_string_split`;
DELIMITER |
CREATE PROCEDURE
P_string_split(IN p_delimiter TEXT, IN p_text TEXT, IN p_limit INT, IN out_table_name TEXT)
BEGIN
DECLARE var_str_text TEXT;
DECLARE var_str_index INT;
DECLARE var_delimiter TEXT;
SET var_delimiter = p_delimiter;
SET var_str_index = 1;
DROP TABLE IF EXISTS TT_P_string_split;
CREATE TEMPORARY TABLE TT_P_string_split
(
str_index INT PRIMARY KEY,
str_text TEXT
);
while(LENGTH(p_text) > 0 AND (p_limit = 0 OR (p_limit > 0 AND p_limit > var_str_index)))
DO
SET var_str_text = SUBSTRING_INDEX(p_text, var_delimiter, 1);
INSERT INTO TT_P_string_split (str_index, str_text) VALUES (var_str_index, var_str_text);
SET var_str_index = var_str_index + 1;
SET p_text = SUBSTRING(p_text, LENGTH(var_str_text) + LENGTH(var_delimiter) + 1);
END WHILE;
IF LENGTH(p_text) > 0 THEN
INSERT INTO TT_P_string_split (str_index, str_text) VALUES (var_str_index, p_text);
END IF;
SET #QUERY_TMP = CONCAT('
DROP TABLE IF EXISTS `',out_table_name,'`;
CREATE TEMPORARY TABLE `',out_table_name,'` (
`str_index` INT PRIMARY KEY,
`str_text` TEXT );
INSERT INTO ',out_table_name,' (str_index,str_text)
SELECT str_index,str_text
FROM TT_P_string_split;');
# Area with problems
# ==================================
SELECT #QUERY_TMP;
PREPARE stmt FROM #QUERY_TMP;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
# ==================================
END |
DELIMITER ;
Execution command :
CALL P_string_split(',', 'plop,plip,plup,plap', 0, 'PLOUP');
Problem is located in : "Area with problems" named after FAREA
If the code in the FAREA is as follows (Variation 1) :
SELECT #QUERY_TMP;
# PREPARE stmt FROM #QUERY_TMP;
# EXECUTE stmt;
# DEALLOCATE PREPARE stmt;
The output is :
#QUERY_TMP
===============================================================
DROP TABLE IF EXISTS `PLOUP`;
CREATE TEMPORARY TABLE `PLOUP` (
`str_index` INT PRIMARY KEY,
`str_text` TEXT );
INSERT INTO PLOUP (str_index,str_text)
SELECT str_index,str_text
FROM TT_P_string_split;
But if the code in the FAREA is complete (Variation 2) :
SELECT #QUERY_TMP;
PREPARE stmt FROM #QUERY_TMP;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
The output is :
#QUERY_TMP
===============================================================
<No row>
And and error is throw :
[42000][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 'CREATE TEMPORARY TABLE `PLOUP` (
`str_index` INT PRIMARY KEY,
' at line 2
I have the impression that the addition of the statement executing part truncates the value of my variable.
Am I missing something?
Thanks in advance.
Edit 1 :
This query :
SELECT *
FROM performance_schema.user_variables_by_thread
WHERE VARIABLE_NAME = 'QUERY_TMP';
Return :
2440,QUERY_TMP,"
DROP TABLE IF EXISTS `PLOUP`;
CREATE TEMPORARY TABLE `PLOUP` (
`str_index` INT PRIMARY KEY,
`str_text` TEXT );
INSERT INTO PLOUP (str_index,str_text)
SELECT str_index,str_text
FROM TT_P_string_split;"
I have fixed the comportment by separate execution.
SET #QUERY_TMP = CONCAT('DROP TABLE IF EXISTS `',out_table_name,'`;');
PREPARE stmt FROM #QUERY_TMP;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET #QUERY_TMP = CONCAT('CREATE TEMPORARY TABLE `',out_table_name,'` (
`str_index` INT PRIMARY KEY, `str_text` TEXT ) ENGINE = InnoDB;');
PREPARE stmt FROM #QUERY_TMP;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET #QUERY_TMP = CONCAT('INSERT INTO ',out_table_name,' (str_index,str_text)
SELECT str_index,str_text
FROM TT_P_string_split;');
PREPARE stmt FROM #QUERY_TMP;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

How to create a create table statement in a loop in mysql

I want to create multiple tables with just one statement (query).
Loop
start
(
create table a
)
a =a +1
end loop
So say it has to create 100 tables labeled as TABLE1, TABLE2, ...
Try the following procedure.
DROP PROCEDURE IF EXISTS `createTableProcTest`;
delimiter //
CREATE PROCEDURE `createTableProcTest`()
BEGIN
DECLARE count INT Default 0;
simple_loop: LOOP
SET #a := count + 1;
SET #statement = CONCAT('Create table Table',#a,' ( name VARCHAR(70), age int );');
PREPARE stmt FROM #statement;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET count = count + 1;
IF count=100 THEN
LEAVE simple_loop;
END IF;
END LOOP simple_loop;
END//
In order to execute just do the following:
Call createTableProcTest();
By executing the above procedure 100 tables will be created having name table1,...,table100.
And the table structure would look like following:
N:B: Procedure execution might take several seconds. Don't be impatient.
You need give us more details but, I think is easier to call a stored procedure, inside the loop, to create the procedure.
You need to create a procedure to create the tables you need and call this procedure inside the loop.
Ex:
CREATE PROCEDURE SP_Create_Table(IN tableName VARCHAR(50)) BEGIN SET
#sql = CONCAT('CREATE TABLE ', tableName, '(column1 INT(11))');
PREPARE stmt FROM #sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; END
Now, call the create table procedure inside the loop
DELIMITER // CREATE FUNCTION CalcIncome ( starting_value INT ) RETURNS
INT BEGIN
DECLARE income INT; SET income = 0; label1: WHILE income <=
50 DO
call SP_Create_Table(CONVERT(VARCHAR(50),starting_value)); END WHILE label1; RETURN income; END; // DELIMITER;

How to use variable as the table identifier in MYSQL

If i have this:
CREATE TABLE ftable (
id INT,
fvalue VARCHAR(14)
);
INSERT INTO ftable VALUES (1,'tableB'),(2,'tableA');
CREATE TABLE tableA (
value VARCHAR(14)
);
SELECT #tmp:=fvalue FROM ftable WHERE id=2;
How do I make it so I can do this:
INSERT INTO #tmp VALUES ('buhambug');
Becuase as far I know that throws a mysql error.Can someone show me a sqlfiddle of the solution? Or maybe I'm thinking about this the wrong way?
You need to use dynamic SQL to use a variable as an object name:
SET #tmp = (SELECT fvalue FROM ftable WHERE id=2);
SET #SQL = CONCAT('INSERT INTO ',#tmp,' VALUES (''buhambug'')');
PREPARE stmt FROM #SQL;
EXECUTE stmt;
SQL FIDDLE
You can't do in static sql.
You can do it in stored procedure:
delimiter $$
drop procedure if exists test_call$$
create procedure test_call(table_in varchar(100))
begin
set #q = concat("select * from ", table_in);
PREPARE stmt FROM #q;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
end$$
delimiter ;
call test_call('TeableA');
drop procedure if exists test_call;
In general dynamic read from dynamic tables is not a good decision

How to get scalar result from prepared statement?

Is it possible to set the result from a prepared statement into a variable? I am trying to create the following stored procedure but it is failing:
ERROR 1064 (42000) at line 31: 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 'stmt USING #m, #c, #a;
DROP PROCEDURE IF EXISTS deleteAction;
DELIMITER $$
CREATE PROCEDURE deleteAction(
IN modul CHAR(64),
IN controller CHAR(64),
IN actn CHAR(64))
MODIFIES SQL DATA
BEGIN
PREPARE stmt FROM 'SELECT id
FROM actions
WHERE `module` = ?
AND `controller` = ?
AND `action` = ?';
SET #m = modul;
SET #c = controller;
SET #a = actn;
SET #i = EXECUTE stmt USING #m, #c, #a;
DEALLOCATE PREPARE stmt;
DELETE FROM acl WHERE action_id = #i;
DELETE FROM actions WHERE id = #i;
END
$$
DELIMITER ;
It may seem strange, but you can assign the variable directly in the prepared statement string:
PREPARE stmt FROM 'SELECT #i := id FROM ...';
-- ...
EXECUTE stmt USING #m, #c, #a;
-- #i will hold the id returned from your query.
Test case:
CREATE TABLE actions (id int, a int);
INSERT INTO actions VALUES (1, 100);
INSERT INTO actions VALUES (2, 200);
INSERT INTO actions VALUES (3, 300);
INSERT INTO actions VALUES (4, 400);
INSERT INTO actions VALUES (5, 500);
DELIMITER $$
CREATE PROCEDURE myProc(
IN p int
)
MODIFIES SQL DATA
BEGIN
PREPARE stmt FROM 'SELECT #i := id FROM actions WHERE `a` = ?';
SET #a = p;
EXECUTE stmt USING #a;
SELECT #i AS result;
DEALLOCATE PREPARE stmt;
END
$$
DELIMITER ;
Result:
CALL myProc(400);
+---------+
| result |
+---------+
| 4 |
+---------+
1 row in set (0.00 sec)
Use this code
PREPARE stmt FROM 'SELECT ''a'' into #i' ;
EXECUTE stmt;
if(#i='a') then
............
end if;
not even sure why you're using dynamic sql in your example - this seems a lot simpler
drop procedure if exists deleteAction;
delimiter #
create procedure deleteAction
(
in p_modul char(64),
in p_controller char(64),
in p_actn char(64)
)
begin
declare v_id int unsigned default 0;
select id into v_id from actions where
module = p_modul and controller = p_controller and action = p_actn;
delete from acl where action_id = v_id;
delete from actions where id = v_id;
select v_id as result;
end #
delimiter ;
call deleteAction('mod','ctrl','actn');

MySQL Pass table name to cursor select

I want the procedure to take parameter answertable and partid in the select statement,
but when i call it it doesn't replace the parameter answertable with the value
the call call updateTotalScores('quiz_participation', 'quiz_answer', 1)
returns the error: 1146 - Table 'quizdb.answertable' doesn't exist
passing the id works, but passing the table name doesn't
so how do i pass the table name to the select in
DECLARE cur1 CURSOR FOR SELECT SUM(`score`), SUM(`maxscore`) FROM answertable WHERE `idParticipation`=partid;
entire procedure:
DELIMITER $$
CREATE PROCEDURE updateTotalScores(IN participationtable CHAR(64), IN answertable CHAR(64), IN partid INT)
BEGIN
DECLARE done INTEGER DEFAULT 0;
DECLARE sscore INTEGER DEFAULT 0;
DECLARE smaxscore INTEGER DEFAULT 0;
DECLARE cur1 CURSOR FOR SELECT SUM(`score`), SUM(`maxscore`) FROM answertable WHERE `idParticipation`=partid;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN cur1;
REPEAT
FETCH cur1 INTO sscore, smaxscore;
UNTIL done = 1
END REPEAT;
CLOSE cur1;
UPDATE participationtable SET `score`=sscore, `maxscore`=smaxscore WHERE `idParticipation`=partid;
END $$
DELIMITER ;
For completeness
the table name cannot be passed to a MySql cursor, at least not yet
http://forge.mysql.com/worklog/task.php?id=3433
the answer from below (corrected a bit)
DELIMITER $$
CREATE PROCEDURE updateTotalScores(IN participation_table VARCHAR(45), IN answer_table VARCHAR(45), IN part_id INT)
BEGIN
SET #stmt_text=CONCAT("SELECT #score := SUM(`score`), #maxscore := SUM(`maxscore`) FROM ",
answer_table, " WHERE `idParticipation`=", part_id);
PREPARE stmt FROM #stmt_text;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET #stmt_text=CONCAT("UPDATE ", participation_table,
" SET `score`=?, `maxscore`=? WHERE `idParticipation`=", part_id);
PREPARE stmt FROM #stmt_text;
EXECUTE stmt USING #score, #maxscore;
DEALLOCATE PREPARE stmt;
END $$
I believe you cannot do it in this manner.
In order to achieve this, you should use Dynamic SQL.
Note that you cannot open a cursor using Dynamic SQL either. But in your case, there seems to be no need for a cursor.
If i understand your code correctly, you can just use user variables and probably achieve what you are trying to do using 2 Dynamically prepared statements.
SET #stmt_text=CONCAT("SELECT #score = SUM(`score`), #maxscore=SUM(`maxscore`) FROM ",
answertable, "WHERE `idParticipation`= ", partid);
PREPARE stmt FROM #stmt_text;
EXECUTE stmt USING #a;
And then you update the values using the below statement
SET #stmt_text=CONCAT("UPDATE", participationtable, " SET `score`=#score,
`maxscore`=#maxscore WHERE `idParticipation`=", partid);
PREPARE stmt FROM #stmt_text;
EXECUTE stmt USING #a;
DEALLOCATE PREPARE stmt;
Note: Please check the syntax. I cannot test it to verify it exactly but i hope you get the idea.