I have an sql script which deletes large number of records from database. Script deletes records by small chunks:
DROP PROCEDURE IF EXISTS `my_proc`;
DELIMITER $$
CREATE PROCEDURE my_proc()
BEGIN
DECLARE chunk_size INT DEFAULT 1000;
main: LOOP
SET #count= -1;
START TRANSACTION;
DELETE FROM my_table WHERE .... LIMIT chunk_size;
SELECT ROW_COUNT() into #count;
COMMIT;
IF #count = 0 THEN
LEAVE main;
END IF;
DO SLEEP(0.1);
END LOOP main;
END$$
DELIMITER ;
CALL my_proc();
DROP PROCEDURE IF EXISTS `my_proc`;
The problem is that DELETE statement may fail, so I want retry deletion of current chunk 10 times. So I want the script to fail only if the DELETE statement fails 10 times for current chunk.
I try to use DECLARE CONTINUE HANDLER FOR 1213 BEGIN ..... END; but do not quite understand how to make it work. The main problem now is to make the script fail with meaningful (preferably original) error message if the DELETE failed 10 times.
Related
Given 2 simple procedures
DELIMITER $$
CREATE PROCEDURE simpleproc()
BEGIN
UPDATE mytable SET name = 'bbb' WHERE id = 1;
DO SLEEP(15);
END
$$ DELIMITER ;
DELIMITER $$
CREATE PROCEDURE simpleproc2()
BEGIN
UPDATE mytable SET name = 'jjj' WHERE id = 1;
END
$$ DELIMITER ;
Then in one connection (in mysql workbench) I run first procedure:
call simpleproc();
and in second connection, run second procedure
call simpleproc2();
What surprised me, is that second transaction no waits for first and executes immediately.
What I missed? why first transaction no locks row id=1?
Table mytable have InnoDB engine
The goal here is to write a script attempts to insert a new category into categories table and if the insert was successful the message:
1 row was inserted
If the update is unsuccessful, the procedure should display this message:
Row was not inserted - duplicate entry
Whenever I run this script, I keep getting the second message no matter how many times I run it, when really it should display the first message, followed by the second. Here is my script. Can someone please help me spot what I'm overlooking? Thank you.
use my_guitar_shop;
/*drop procedure if exists add_category;*/
DELIMITER //
CREATE PROCEDURE add_category(
in categories VARCHAR(100)
)
begin
declare duplicate_entry_for_key TinyINT DEFAULT FALSE;
declare continue handler for 1062
SET duplicate_entry_for_key = TRUE;
insert into categories values (5, 'Electric');
select '1 row was inserted.' as message;
if duplicate_entry_for_key = true then
select 'Row was not inserted - duplicate entry.' as message;
end if;
end //
DELIMITER ;
/* call the stored procedure with 'Gibson' */
call add_category('Gibson');
call add_category('Gibson');
You should set duplicate_entry_for_key to True. If data insert is successful.
Do something like below after insert operation.
SET duplicate_entry_for_key = last_insert_id();
OR see the below example for bit check.
For example:
START TRANSACTION; -- Begin a transaction
INSERT INTO categories
VALUES
(
5
,'Electric'
);
IF ROW_COUNT() > 0 THEN -- ROW_COUNT() returns the number of rows updated/inserted/deleted
SET duplicate_entry_for_key = TRUE;
COMMIT; -- Finalize the transaction
ELSE
SET duplicate_entry_for_key = False;
--You can ROLLBACK the transaction also - Revert all changes made before the transaction began
END IF
I have MySQL stored procedure that will create temporary table from remote server using FEDERATED tables. Then the procedure used these temporary table to do some work.
I am seeing some issue where I get an error like
Unable to connect to foreign data source
Got timeout reading communication packets
Unable to connect to foreign data source
Got error 1430 from storage engine
The problem here is that if these temporary table failed to create the entire procedure fails because the tables do not exists.
So I thought I can do some checking after the attempt to create temporary table and if the attempt return error then I go back and try it again. to stop an infinite loop I added a condition to quite after 10 tries.
The issue that i am running into is that the script quits and it does not run all the way to the end.
here is a portion of the procedure where the procedure is quitting and not sure why.
DELIMITER $$
CREATE DEFINER=`root`#`10.%` PROCEDURE `act`()
MAIN:
BEGIN
DECLARE current_procedure_name CHAR(60) DEFAULT 'activities';
DECLARE last_run_time DATETIME DEFAULT NULL;
DECLARE current_run_time_start DATETIME DEFAULT NOW();
-- set the SQL mode to ''
SET SQL_MODE = '';
-- set MySQL Safe mode OFF on update
SET SQL_SAFE_UPDATES = 0;
SET #trys = 0;
loop_label: LOOP
SET #trys := #trys+1;
-- db.view_users is a federated table
DROP TEMPORARY TABLE IF EXISTS view_users1, view_users2, view_users3;
CREATE TEMPORARY TABLE view_users1 (KEY(user_id)) ENGINE=MEMORY AS
SELECT user_id, fullname
FROM db.view_users;
IF(##error_count > 0 OR #trys > 10) THEN
-- if there are too many tries to create the temporary table and it keeps failing Quit!!
IF( #trys > 10) THEN
LEAVE MAIN;
ELSE
-- sleep for 10 seconds and go back to create the table again
SELECT SLEEP(10);
ITERATE loop_label;
END IF;
END IF;
CREATE TEMPORARY TABLE view_users2 (KEY(user_id)) ENGINE=MEMORY AS
SELECT * FROM view_users1;
IF(##error_count > 0) THEN
ITERATE loop_label;
END IF;
CREATE TEMPORARY TABLE view_users3 (KEY(user_id)) ENGINE=MEMORY AS
SELECT * FROM view_users1;
IF(##error_count > 0) THEN
ITERATE loop_label;
END IF;
END LOOP;
-- set MySQL Safe mode back ON on update
SET SQL_SAFE_UPDATES = 1;
END
How can I find out why it is quitting? it seem that #trys is reaching 11 and it quits but I don't understand why would it?
I have tried to run this code outside the procedure and the second line returns 0 error;
DROP TEMPORARY TABLE IF EXISTS view_users1, view_users2, view_users3;
CREATE TEMPORARY TABLE view_users1 (KEY(user_id)) ENGINE=MEMORY AS
SELECT user_id, fullname
FROM db.view_users;
SELECT ##error_count;
A second question, is there a better approach for this problem? it is important that this procedure runs all the way.
I finally figure out the cause of the issue.
from the manual 13.6.5.5 LOOP Syntax
The statements within the loop are repeated until the loop is
terminated. Usually, this is accomplished with a LEAVE statement.
Within a stored function, RETURN can also be used, which exits the
function entirely
at the end just before END LOOP; I needed to add LEAVE loop_label to end the LOOP other wise it will continue until #trys reaches 11;
The Question
purely for academic reasons, I'm wondering if you could add a handler to a mysql stored procedure that is able to recover from a lock wait timeout error if one of its queries locks up (such as a SELECT ... FOR UPDATE or UPDATE) query.
The Example
This is assuming an innoDB database, set to issolation level Repeatable read, with an empty users table defined.
1. Example Procedure:
DROP PROCEDURE IF EXISTS `lock_test`;
DELIMITER ;;
CREATE PROCEDURE `lock_test`(OUT status ENUM('success','timeout'))
MODIFIES SQL DATA
BEGIN
START TRANSACTION;
SELECT * FROM `users` FOR UPDATE;
SET status := 'success';
COMMIT;
END;;
DELIMITER ;
2. Run code in mysql terminal 1:
START TRANSACTION;
SELECT * FROM `users` FOR UPDATE;
the contents of users will be displayed, but the transaction will remain open.
3. Run code in mysql terminal 2:
CALL `lock_test`(#out);
SELECT #out;
the transaction will run until it times out (default value of innodb_lock_wait_timeout is 50 seconds)
Is it possible to add a handler inside the lock_test() procedure, so that we can have #out hold 'timeout'?
After spending some time reading through the MySQL Handler Documentation I was able to get what I was looking for:
DROP PROCEDURE IF EXISTS `lock_test`;
DELIMITER ;;
CREATE PROCEDURE `lock_test`(OUT status_out VARCHAR(255))
MODIFIES SQL DATA
BEGIN
DECLARE procedure_attempts INT DEFAULT 5;
DECLARE query_timeout INT DEFAULT FALSE;
SET status_out := 'start';
procedure_loop:
REPEAT
BEGIN
DECLARE CONTINUE HANDLER FOR 1205
-- Error: 1205 SQLSTATE: HY000 (ER_LOCK_WAIT_TIMEOUT)
BEGIN
SET query_timeout := TRUE;
SET status_out := CONCAT(status_out,'-timeout');
END;
IF ( procedure_attempts < 1) THEN
LEAVE procedure_loop;
END IF;
START TRANSACTION;
SELECT * FROM `users` FOR UPDATE;
IF (query_timeout) THEN
SET query_timeout := FALSE;
ELSE
SET status_out := CONCAT(status_out,'-success');
SET procedure_attempts := 0;
END IF;
COMMIT;
SET procedure_attempts := procedure_attempts - 1;
END;
UNTIL FALSE END REPEAT;
-- loop
SET status_out := CONCAT(status_out,'-end');
END;;
DELIMITER ;
When run as follows:
SET ##innodb_lock_wait_timeout:=1;
CALL `lock_test`(#out);
SELECT #out;
The output will be start-timeout-timeout-timeout-timeout-timeout-end after about 10 seconds of running time (which would be much longer if run without setting the timeout to 1 second.
While probably not too practical (or advisable) in most projects, could potentially be useful when debugging timeout issues when running a query from inside another query - I hope it might help someone else in the future.
I have written a procedure that creates a temporary table and executes a query by fetching the rows from the temporary table.I have around 13486 rows in the temporary table.But when i am calling the procedure i observed that the procedure is getting terminated after fetching 107 rows from the temporary table.Moreover i also observed that this value is not constant..Sometimes it is 107 the other time it is 114 and some other time it is just 100.Why this happens?Please need help?Somebody please..Here is my procedure.And i came to know that while loop will terminate for >1000 iterations.Please suggest me a method to overcome this.
DELIMITER $$
DROP PROCEDURE IF EXISTS `lookup`.`test` $$
CREATE PROCEDURE `lookup`.`test` ()
BEGIN
CREATE TEMPORARY TABLE lookup.airportname(id int AUTO_INCREMENT,PRIMARY KEY(id))
AS (select distinct airport_id from lookup.airport);
SET #num=0;
SET #arpt=NULL;
SELECT count(*) INTO #num FROM airportname;
SET #i=0;
while #i<#num do
SELECT airport_id INTO #arpt FROM airportname WHERE id=#i;
select #arpt,#i;
set #i=#i+1;
end while;
END $$
DELIMITER ;
I am using mysql query browser.Thank you.
If you want to insert value into id Then it should not be auto_increment. - Remove auto_increment from table definition, it should work
delimiter |
create procedure employee_select()
Begin
Declare empno,done int(9);
Declare emp_select Cursor for select emp_no from gross_salary ;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
Open emp_select; // cursor opening
read_loop: loop // start looping all the datas one by one
fetch emp_select into empno; // fetching the select value into variable empno
//note :variable name should not be same as columne name in select statement"
IF done THEN
LEAVE read_loop; // if no more rows, this makes it to leave the loop"
END IF;
//Enter the code you want do for each row
end loop;
close emp_select;
End |
delimiter ;