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
Related
I have sample procedure where I'm passing DB names as input param and it was looping fine and updating the statements.
Please find the Below one :
DELIMITER &&
CREATE PROCEDURE update_stmt (IN DBName varchar(100))
BEGIN
DECLARE _next TEXT DEFAULT NULL;
DECLARE _nextlen INT DEFAULT NULL;
DECLARE _value TEXT DEFAULT NULL;
DECLARE schemaname varchar(64);
iterator:
LOOP
IF CHAR_LENGTH(TRIM(_list)) = 0 OR _list IS NULL THEN
LEAVE iterator;
END IF;
SET _next = SUBSTRING_INDEX(_list,',',1);
SET _nextlen = CHAR_LENGTH(_next);
SET _value = TRIM(_next);
SET schemaname = _value;
SELECT concat('update ',schemaname, '.SAMPLE SET COL = 0 where ID IN (',GROUP_CONCAT(TICKET),');') AS statement
FROM Test.SAMPLE
INTO #sql;
PREPARE stmt from #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET _list = INSERT(_list,1,_nextlen + 1,'');
END LOOP;
END
When the Procedure executes it will create statement like the below if I print:
Update Test.sample SET COL = 0 WHERE ID IN (1,2,3)
For Example If I have ID Column like this in the table :
**ID**
1A
2B
3C
When I execute the procedure I expect to get the statement like below :
Update Test.sample SET COL = 0 WHERE ID IN ('1A','2B','3C')
But I'm getting error as
Unnkown column 1A in where clause
In the Groupconcat how I need to handle alphanumeric values and one more thing FROM Test.SAMPLE.
I'm passing variable in the place of DB Name it is giving error as schemaname.SAMPLE doesn't exist.
Please suggest
Provided ID column is a char string, add single quotes around the TICKET value .. GROUP_CONCAT(CONCAT('''', TICKET, '''')) ..
Also check if #sql is null (Test.SAMPLE is empty)
..
if #sql is not null then
PREPARE stmt from #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
end if;
To stop the proc gracefully at error provide an exit handler for SQLEXCEPTION. For example
CREATE PROCEDURE update_stmt (IN DBName varchar(100))
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
/* Your error processing here */
END;
..
db-fiddle
I am mass assigning new id numbers to things in the DB to make room for some stuff at the beginning of each table. I created a procedure that works, but when I try adding input parameters to allow scripting, it can't find the table
delimiter |
CREATE PROCEDURE changeID
( IN in_table_name varchar(64))
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE a,b INT DEFAULT 800000;
DECLARE cur1 CURSOR FOR SELECT id FROM in_table_name ORDER BY id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO b;
IF done THEN
LEAVE read_loop;
END IF;
UPDATE in_table_name SET id = a + 1 where id = b;
SET a = a+1;
END LOOP;
CLOSE cur1;
END;
|
delimiter ;
When I run this using call changeID('users'), I get the error:
[Err] 1146 - Table 'databaseName.in_table_name' doesn't exist
I was hoping to loop through using a simple list of commands like this so it could run unattended instead of manually changing the in_table_name between each execution:
call changeID('users');
call changeID('appointments');
call changeID('groups');
You can't dynamically pass a table name in a query, however, you can concatenate a string and then execute it as a statement. You of course want to be careful and ensure that this data has been sanitized etc. I wasn't able to test this, but something to this effect should get you going.
...
END IF;
SET #Query = CONCAT('UPDATE ',in_table_name,' SET `id` = ',a+1,' WHERE `id`=',b);
PREPARE stmt FROM #Query;
EXECUTE stmt;
...
https://dev.mysql.com/doc/refman/5.7/en/sql-syntax-prepared-statements.html
KChason got me started in the right direction, but I had to take it a little further to get the first part working from tips here: https://forums.mysql.com/read.php?98,138495,138908#msg-138908.
DROP PROCEDURE
IF EXISTS `workingversion`;
delimiter |
CREATE PROCEDURE `workingversion` (IN tableName VARCHAR(100))
BEGIN
DECLARE done INT DEFAULT 0 ;
DECLARE a,
b INT DEFAULT 800000 ;
DROP VIEW IF EXISTS v1;
SET #stmt_text = CONCAT("CREATE VIEW v1 AS SELECT id FROM ", tableName, " ORDER BY id") ;
PREPARE stmt
FROM
#stmt_text ; EXECUTE stmt ; DEALLOCATE PREPARE stmt ;
BEGIN
DECLARE cur1 CURSOR FOR SELECT * FROM v1 ;
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000'
SET done = 1 ; OPEN cur1 ;
REPEAT
FETCH cur1 INTO b ;
IF NOT done THEN
SET #Query = CONCAT('UPDATE ',tableName,' SET `id` = ',a+1,' WHERE `id`=',b);
PREPARE stmt FROM #Query;
EXECUTE stmt;
SET a = a+1;
END
IF ; UNTIL done
END
REPEAT
; CLOSE cur1 ;
END ;
END
I have 2 schemas with exactly the same tables schema_oltp and schema_olap. I want to delete from selected tables in schema_oltp only the data which already exists in schema_olap. The tables I want to delete from are listed in a table called archived_to_delete. For each of the tables listed in archived_to_delete, I need to delete the data in batches of 100 000. This is what I have so far. It only deletes 100 000 in each table and stops. I want it to continue until there is no data in schema_oltp which also exists in schema_olap:
DELIMITER $$
CREATE DEFINER=`root`#`%` PROCEDURE `schema_oltp`.`schemaDeletes`(IN from_schema VARCHAR(100), IN to_schema VARCHAR(100))
BEGIN
DECLARE v_tname varchar(100);
DECLARE v_pkey varchar(455);
DECLARE v_upfield varchar(455);
DECLARE done INT DEFAULT 0;
DECLARE v_limit INT DEFAULT 100000;
DECLARE v_deleted INT;
DECLARE table_cursor CURSOR FOR SELECT a.table_name, a.column_name
FROM INFORMATION_SCHEMA.COLUMNS a
INNER JOIN archived_to_delete b
ON a.table_name = b.table_name
WHERE TABLE_SCHEMA = 'schema_oltp'
AND COLUMN_KEY='PRI'
ORDER BY b.archived_to_delete_id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN table_cursor;
REPEAT
FETCH table_cursor INTO v_tname, v_pkey;
IF NOT done THEN
SET v_deleted=0;
SET #sql = CONCAT('DELETE FROM ',from_schema,'.', v_tname, ' WHERE ',v_pkey,' IN (SELECT ', v_pkey, ' FROM ',to_schema,'.', v_tname, ') limit ',v_limit,';');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET v_deleted=v_deleted+1;
IF v_deleted = v_limit THEN
commit;
SET v_deleted=0;
END IF;
SELECT CONCAT('Deleting...',v_tname);
END IF;
UNTIL done END REPEAT;
CLOSE table_cursor;
END$$
DELIMITER ;
i'm trying to slice a long double Column by Varied size , and insert it with cursor to new table that has this size of columns amount.
the trouble is the cursor isn't responding?
I tried to activate the cursor (Externally) manually And managed to.
So I have the impression cursor does not work within procedure?!
Here's the code:
The outer procedure:
CREATE DEFINER=`root`#`localhost` PROCEDURE `slice_arrays`(IN `size` INT)
DETERMINISTIC
BEGIN
DECLARE n INT DEFAULT 0;
DECLARE h INT DEFAULT 0;
DECLARE q_creat text DEFAULT "";
DECLARE cell text DEFAULT "cell";
DECLARE cell_type text DEFAULT "double";
DECLARE new_table_name text DEFAULT "";
set new_table_name=CONCAT("table_1440_slice_by_",size);
SELECT COUNT(*) FROM eurusd_1440_minute_timeframe_chart_bars INTO n;
SET #q_creat =CONCAT("CREATE TABLE IF NOT EXISTS ",new_table_name," (");
WHILE h < size DO
SET #q_creat = CONCAT(#q_creat,cell,h," ",cell_type);
set #h_pluse_1=h+1;
if #h_pluse_1 = size THEN
SET #q_creat= CONCAT(#q_creat,")");
else
SET #q_creat= CONCAT(#q_creat,",");
end if;
set h=h+1;
END WHILE;
PREPARE stmt1 FROM #q_creat;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
TRUNCATE TABLE new_table_name;
set h=0;
set #temp_cuonter=size;
WHILE #temp_cuonter <= n DO
drop table IF EXISTS temp_table;
create table temp_table as (SELECT * FROM table_1440_test3 LIMIT h , size);
CALL insert_into_selected_table_from_temp_table(new_table_name);
set h=h+1;
set #temp_cuonter =#temp_cuonter+1;
TRUNCATE TABLE tamp_table;
end while;
End
The nested procedure:
CREATE DEFINER=`root`#`localhost` PROCEDURE `insert_into_selected_table_from_temp_table`(IN `table_name` TEXT)
DETERMINISTIC
begin
set #insert_q= CONCAT("INSERT INTO ",table_name," VALUES(");
set #insert_q= CONCAT(#insert_q,get_temp_table(),")");
PREPARE stmt2 FROM #insert_q;
EXECUTE stmt2;
DEALLOCATE PREPARE stmt2;
end
thanks!
I put the cursor declaration in the prepared statement and then executed it, then returns an error #1324 - Undefined CURSOR: getid.
How do I solve this problem?
delimiter ;;
drop procedure if exists test2;;
create procedure test2(table_id VARCHAR(25))
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE id INT;
DECLARE id_new INT;
DECLARE stmt1 VARCHAR(1024);
DECLARE stmt2 VARCHAR(1024);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
SET #sqltext1 := CONCAT('DECLARE getid CURSOR FOR SELECT entryId FROM ',table_id,' ORDER BY entryId');
PREPARE stmt1 FROM #sqltext1;
EXECUTE stmt1;
SET #id_new = 1;
OPEN getid;
FETCH getid into id;
REPEAT
SET #sqltext2 := CONCAT('UPDATE ',table_id,' SET entryId = ? WHERE entryId = ?');
PREPARE stmt2 FROM #sqltext2;
EXECUTE stmt2 USING #new_id, id;
SET #id_new = #id_new + 1;
FETCH getid into id;
UNTIL done END REPEAT;
CLOSE getid;
END
;;
CALL test2('Test');
Some rules:
All declarations must be at one place in a sequence.
You can't use variable names in cursor declarations.
Handler declarations must be after cursor declarations.
You can't use local variable names (id) as bound parameters for
prepared statements. You can only use session variables (say #_id).
To overcome such problems, you can adopt following solution.
Define a temporary table using the input parameter to the SP.
Now declare the cursor on the same table and use it.
Drop the temporary table created.
Following example should work on your tables.
delimiter $$
drop procedure if exists test2$$
create procedure test2( table_id varchar(25) )
begin
set #temp_query = 'drop temporary table if exists temp_cursor_table';
prepare pst from #temp_query;
execute pst;
drop prepare pst; -- or
-- deallocate prepare pst;
set #temp_table_query='create temporary table temp_cursor_table ';
set #temp_table_query=concat( #temp_table_query, ' select entryId from ' );
set #temp_table_query=concat( #temp_table_query, table_id );
set #temp_table_query=concat( #temp_table_query, ' order by entryId' );
prepare pst from #temp_table_query;
execute pst;
drop prepare pst;
-- now write your actual cursor and update statements
-- in a separate block
begin
declare done int default false;
declare id int;
declare id_new int;
declare stmt1 varchar(1024);
declare stmt2 varchar(1024);
declare getid cursor for
select entryId from temp_cursor_table order by entryId;
declare continue handler for not found set done = 1;
set #id_new = 1;
open getid;
fetch getid into id;
repeat
set #sqltext2 := concat( 'update ', table_id );
set #sqltext2 := concat( #sqltext2, ' set entryId = ? where entryId = ?' );
set #_id = id;
prepare stmt2 from #sqltext2;
execute stmt2 using #new_id, #_id;
set #id_new = #id_new + 1;
fetch getid into id;
until done end repeat;
close getid;
end;
end;
$$
delimiter ;
Now call the procedure with table_id value.
call test2( 'Test' );