I need help with a rollback. I need to insert in table CATEGORIA ONLY when I insert into CATEGORIA_IDIOMA. So I tried a TRANSACTION, but it does not work, I have a UNIQUE CONSTRAINT IN CATEGORIA_IDIOMA FOR (id_idioma, nombre_categoria). The code for my STORED PROCEDURE is:
DELIMITER //
CREATE PROCEDURE sp_ingresar_categoria
(
IN AV_CATEGORIA_NOMBRE VARCHAR(255),
IN AI_ID_IDIOMA INT
)
BEGIN
DECLARE EXIT handler for sqlexception
BEGIN
SELECT 'EXISTE UN ERROR, NO SE PUDO INGRESAR LA CATEGORIA'
ROLLBACK;
END;
START TRANSACTION;
INSERT INTO `CATEGORIA`(`activo`)
VALUES (1);
SELECT MAX(`id_categoria`) INTO #LI_CATEGORIA from `CATEGORIA`;
INSERT INTO `CATEGORIA_IDIOMA`(`id_categoria`, `id_idioma`, `categoria_nombre`)
VALUES(#LI_CATEGORIA, AI_ID_IDIOMA, AV_CATEGORIA_NOMBRE);
commit;
END //
When I try it with a new value, it works fine, but when I try it with a duplicated value, it does not insert into CATEGORIA_IDIOMA, but it inserts into CATEGORIA.
Can someone explain me how it works, or where is my error please.
Related
I have this small script in my populate.sql which is supposed to roll back all inserts if any fails. However im not being able to execute populate.sql through "source populate.sql" cause it says i have a syntax error on my handler, but i've already check mysql 5.7 docs and everything seems all right. What am i doing wrong?
DELIMITER $$
CREATE PROCEDURE foo()
BEGIN
DECLARE rollbacka BOOL DEFAULT 0;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET rollbacka = 1;
START TRANSACTION;
insert into obra values('101-903aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa','101-903.jpeg');
insert into obra values('101-904','101-904.jpeg');
insert into obra values('101-905','101-905.jpeg');
insert into obra values('101-906','101-906.jpeg');
insert into obra values('101-907','101-907.jpeg');
IF rollbacka THEN
ROLLBACK;
ELSE
COMMIT;
END IF;
END $$
DELIMITER ;
select * from museu.obra;
[update]Here's what im testing. It Shouldn't update table obras cause first attribute of the first insert is too big, so it will throw an exception, however when i comment it, the table remains empty, in that situation the other 4 inserts should work.
I am very new in Mysql, probably don't know or don't understand something essential.
Could you please advise me why 'begin !!!' message is not inserted in this
case?
DELIMITER $$
CREATE TABLE `_debugLogTable` (
`Message` varchar(255) DEFAULT NULL
) ENGINE=InnoDB $$
CREATE PROCEDURE `debug_msg`(msg VARCHAR(255))
BEGIN
insert into _debugLogTable select msg;
END$$
CREATE FUNCTION `ValueMeetsCondition`(value varchar(20)) RETURNS tinyint(1)
BEGIN
DECLARE ConditionValue INTEGER;
call debug_msg('begin !!!');
SET ConditionValue = CAST(`value` AS UNSIGNED);
call debug_msg('end !!!');
RETURN TRUE;
END$$
DELIMITER ;
I am aware that CAST function fails, but why call debug_msg('begin !!!'); does not insert new record into table?! There are not any transactions there!
Just want post an answer, maybe it will help somebody in the future.
From this we have -
If autocommit mode is enabled, each SQL statement forms a single transaction on its own. By default, MySQL starts the session for each new connection with autocommit enabled, so MySQL does a commit after each SQL statement if that statement did not return an error. If a statement returns an error, the commit or rollback behavior depends on the error
I call function in this way - select ValueMeetsCondition('>10').
So actually it is wrapped into transaction by MySQL, that's why if something inside my procedure fails - the whole changes are roll backed.
If i remake my query in this way the message begin !! will be inserted, while end !! does not
call debug_msg('begin !!!');
SET ConditionValue = CAST(`>10` AS UNSIGNED);
select ConditionValue;
call debug_msg('end !!!');
So here's the stored procedure I've written. When I ran the DELETE and UPDATE in a single sql tab
as:
DELETE FROM curriculumsubjects WHERE curriculumId = 27;
INSERT INTO curriculumsubjects(curriculumId,subjectCode)
VALUES(27,'MATH101');
it works. It executes delete and insert without any problem
But if I call the stored procedure as:
CALL `enrollmentdb`.`updateCurriculumSubjects`(27, 'MATH101');
it returns the 'error' string i put during ROLLBACK
What could be causing the failure of transaction within the stored procedure body when it runs successfully if ran without stored procedure CALL?
Here's the stored procedure.
CREATE DEFINER=`root`#`localhost` PROCEDURE `updateCurriculumSubjects`(IN p_curriculumId int, IN p_subjectCode varchar(100))
BEGIN
DECLARE hasError BOOLEAN DEFAULT 0;
DECLARE CONTINUE HANDLER FOR sqlexception SET hasError = 1;
START TRANSACTION;
DELETE FROM curriculumsubjects WHERE curriculumId = p_curriculumId;
INSERT INTO curriculumsubjects(curriculumId,subjectCode)
VALUES(p_curriculumId,p_subjCode);
IF hasError THEN
ROLLBACK;
SELECT 'error';
ELSE
COMMIT;
END IF;
END
By the way I'm using Mysql Workbench 6.3 and what I'm trying to do is to delete all the columns matching the curriculumId before I insert again.
On Java, I'll be iterating the call to the stored procedure for multiple inserts.
I hope you can help. I just can't find a reason why delete and insert won't work if put within a transaction.
Thanks.
You have just a mistake in your insert syntax which you have written p_subjCode but your input variable is p_subjectCode and I change Boolean type variable to tinyint(1) for more version support.
CREATE DEFINER=`root`#`localhost` PROCEDURE `updateCurriculumSubjects`(IN p_curriculumId int, IN p_subjectCode varchar(100))
BEGIN
DECLARE hasError TINYINT(1) DEFAULT 0;
DECLARE CONTINUE HANDLER FOR sqlexception SET hasError = 1;
START TRANSACTION;
DELETE FROM curriculumsubjects WHERE curriculumId = p_curriculumId;
INSERT INTO curriculumsubjects(curriculumId,subjectCode)
VALUES(p_curriculumId,p_subjectCode);
IF hasError=1 THEN
ROLLBACK;
SELECT 'error';
ELSE
COMMIT;
END IF;
END
I am using Stored procedures to do CRUD operations on Mysql DB
DELIMITER $$
CREATE PROCEDURE `transaction_sp` ()
BEGIN
DECLARE exit handler for sqlexception
BEGIN
-- ERROR
ROLLBACK;
END;
DECLARE exit handler for sqlwarning
BEGIN
-- WARNING
ROLLBACK;
END;
START TRANSACTION;
INSERT INTO table_name (id, name, address) values ('1','Test','xpert.com');
SET #LID = LAST_INSERT_ID();
INSERT INTO table_name3 (id, job, responsibilities) values
(#LID,'Sr. Developer','Coding,mentoring etc');
COMMIT;
END
$$
Now, i want that if Second INSERT statement fails then second SQL statement shall not execute and first Insert shall be rolled back.
With my above approach 1st transaction is not rolled back. Do i need to set any flags?
How to handle it? Well explained answer will help here.
Please see my answer below. I believe if you check ##ROWCOUNT or ##Error after each insert you can rollback if there is no rows affected. Sorry if the method I used is wrong.
DELIMITER $$
CREATE PROCEDURE `transaction_sp` ()
BEGIN
DECLARE #nRowCount1 int,
#nRowCount2 int
DECLARE exit handler for sqlexception
BEGIN
-- ERROR
ROLLBACK;
END;
DECLARE exit handler for sqlwarning
BEGIN
-- WARNING
ROLLBACK;
END;
START TRANSACTION;
INSERT INTO table_name (id, name, address) values ('1','Test','xpert.com');
SET #LID = LAST_INSERT_ID();
SET #nRowCount1 = ##ROWCOUNT ;
INSERT INTO table_name3 (id, job, responsibilities) values
(#LID,'Sr. Developer','Coding,mentoring etc');
SET #nRowCount2 =##ROWCOUNT
if(#nRowCount1 <=0 and #nRowCount2<=0)
begin
ROLLBACK ;
end
COMMIT;
END
$$
Can someone tell me if it is possible to call another procedure from within a procedure and if any part of either procedure fails, roll everything back?
If this is possible, can someone please show me a tiny example of how this would be implemented?
EDIT: Procedure "b" fails but procedure "a" still inserts a row into table "a". It's my understanding that if any part of the insert fails that everything (both inserts) is rolled back which is not happening here. The questions is why not?
Procedure "a"
BEGIN
DECLARE b INT DEFAULT 0;
DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK;
DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK;
START TRANSACTION;
INSERT INTO a(a)
VALUES(iA);
CALL b(iB,LAST_INSERT_ID(),#b);
SELECT #b INTO b;
IF b !=1 THEN
ROLLBACK;
ELSE
COMMIT;
END IF;
END
Procedure "b"
BEGIN
DECLARE b INT DEFAULT 0;
DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK;
DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK;
START TRANSACTION;
INSERT INTO b VALUES(iB,id);
SET b=1;
COMMIT;
END;
You will need to handle transactions in both procedures, but the proc that is calling the other, should check for the return value and rollback it's transactions based on that. Here is an example of the inner proc:
How to detect a rollback in MySQL stored procedure?
you would then check for p_return_code and do a rollback of the parent transaction.
EDIT:
What I think is happening is that inner SP COMMIT or ROLLBACK affect outer SP TRANSACTION. This code works for me, if inner SP fail it rolls back both insert statements. First call to ab() works, new user record gets inserted and new game record gets inserted, if we remove record from the games table and run ab() again, because user id already exists it rolls back games table insert:
create procedure ab()
BEGIN
START TRANSACTION;
INSERT INTO games (title) VALUES ('bad game');
CALL ba(#ret);
IF #ret!=0 THEN
ROLLBACK;
ELSE
COMMIT;
END IF;
END;
create procedure ba(OUT return_value tinyint unsigned)
BEGIN
DECLARE exit handler for sqlexception
BEGIN
set return_value = 1;
END;
INSERT INTO users (id) VALUES(1);
set return_value = 0;
END;
To test use call ab();