Why is the SQL transaction not being rolled back? - sql-server-2008

Here is the script with the unnecessary parts striped out
USE databaseName
BEGIN TRY
DECLARE #count INT
DECLARE #ErrorMsg VARCHAR(MAX)
SET #count=(SELECT COUNT(*)
FROM xxxtable
WHERE xxxcolumn = 'xxx')
IF( #count = 0 ) --This means that the script has not been run yet
BEGIN
BEGIN TRANSACTION
--do work in here
COMMIT TRANSACTION
END
ELSE
BEGIN
SELECT 'This script has already been run before. Cannot run it again.'
END
END TRY
BEGIN CATCH
IF( Xact_state() <> 0 )
BEGIN
ROLLBACK TRAN
PRINT( 'ROLLED BACK TRANSACTION' )
SELECT Error_number() AS error_number,
Error_line() AS error_line,
Error_message() AS error_message
END
END CATCH
When the script fails, it enters the catch block and prints ROLLED BACK TRANSACTION and also displays the select statement results (error message etc).
But when I check in the database, the data until the point of failure is committed. What am I doing wrong here?
Update: After rolling back the data, the latest identity value available changes. (Suppose the highest identity available is 10, if I insert and rollback, the highest identity available is 11 and not 10 anymore). So the roll back is does not return the database to the state before the transaction. This is what was causing the problems.

Based on your update re: IDENTITY value, this is expected behaviour. A rollback will not reset the IDENTITY value back, so it will generate gaps in your ID values.
Presumably that's what you mean when you say "the data until the point of failure is committed" - and not that all the data you've updated/inserted in the transaction is still there after the supposed rollback.

Related

Using IF-THEN-ELSE statement in a mySQL transaction

I am learning MySQL and i have a simple transaction to make. Need to deduct quantity from stock, if there is sufficient stock for a specific item. Else, need to roll back the whole transaction.
I have tried the following code :
SET #item_code = 'PEN-001';
set #balance = (SELECT quantity_in_hand FROM items WHERE item_code = 'PEN-001');
-- ------------------------------------------------
DELIMITER //
START TRANSACTION;
IF (#balance) >= 0 THEN
BEGIN
UPDATE items SET quantity_in_hand = #balance - #purchaseqty WHERE item_code = 'PEN-001'
END;
ELSE
BEGIN
ROLLBACK;
SELECT 'Insufficient Stock';
END;
END IF;
COMMIT;
DELIMITER ;
When I run this query in MySQL, nothing happens, there is no action output or error displayed either. Is this the right approach? And, What is the issue with this code?
You could do:
UPDATE items
SET quantity_in_hand = quantity_in_hand - #purchaseqty
WHERE item_code = 'PEN-001' and quantity_in_hand>=#purchaseqty
and then check ROW_COUNT()
The transaction in your example does nothing as rollback has nothing to do and the update when it happens, is atomic by default.

Mysql - Spring Boot - Unknown delay between Stored procedure execution from code and actual start

I have a Spring Boot application with mysql as db. To achieve some requirement, I have implemented a stored procedure in MySql and triggering it through my spring boot code.
However, what I am observing is, After my spring boot code is triggered, mysql is idle for around 90 seconds. After that procedure execution starts. This is slowing the system down. Any suggestions if this is expected or I am doing something wrong.
Below is my spring code to invoke procedure
#PersistenceContext
private EntityManager entityManager;
#Transactional
public void backFillClosingStock(String dbName, String id_list) throws Exception {
try {
ThreadLocalStorage.setTenantName(dbName);
log.info("Starting backfilling closing stock");
StoredProcedureQuery storedProcedure = entityManager.createStoredProcedureQuery("update_closing_stock");
storedProcedure.registerStoredProcedureParameter("id_list", String.class, ParameterMode.IN);
storedProcedure.setParameter("id_list", id_list );
storedProcedure.execute();
ThreadLocalStorage.setTenantName(null);
log.info("Completed backfilling closing stock");
} catch (Exception e) {
throw new Exception("something went wrong!");
}
And below is my stored procedure.
DELIMITER //
DROP PROCEDURE IF EXISTS update_closing_stock//
CREATE PROCEDURE update_closing_stock(id_list TEXT)
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE entryid1 decimal;
DECLARE oldClosingStock decimal;
DECLARE newClosingStock decimal;
DECLARE isValueChanged INT DEFAULT 0;
DECLARE cur CURSOR FOR SELECT entryid FROM inward_outward_entries WHERE FIND_IN_SET(productId,id_list)>0 AND is_deleted=0;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
SET autocommit=0;
TRUNCATE temp;
INSERT INTO temp VALUES (CONCAT('Procedure Started ',SYSDATE()));
OPEN cur;
ins_loop: LOOP
FETCH cur INTO entryid1;
IF done THEN
LEAVE ins_loop;
END IF;
SELECT
bs1.closingStock as oldClosingStock,
CASE WHEN bs1.type='Inward' THEN(
(SELECT CASE WHEN SUM(bs2.quantity) IS NULL THEN 0 ELSE SUM(bs2.quantity) END FROM backfill_closing_stock bs2
WHERE bs2.id>=bs1.id AND bs2.type='Inward' AND bs1.Productid=bs2.productid AND bs2.warehouseid=bs1.warehouseid)
-
(SELECT CASE WHEN SUM(bs2.quantity)IS NULL THEN 0 ELSE SUM(bs2.quantity) END FROM backfill_closing_stock bs2
WHERE bs2.id>bs1.id AND bs2.type!='Inward' AND bs1.Productid=bs2.productid AND bs2.warehouseid=bs1.warehouseid)
)
ELSE (
(SELECT CASE WHEN SUM(bs2.quantity) IS NULL THEN 0 ELSE SUM(bs2.quantity) END FROM backfill_closing_stock bs2
WHERE bs2.id>bs1.id AND bs2.type='Inward' AND bs1.Productid=bs2.productid AND bs2.warehouseid=bs1.warehouseid)
-
(SELECT CASE WHEN SUM(bs2.quantity) IS NULL THEN 0 ELSE SUM(bs2.quantity) END FROM backfill_closing_stock bs2
WHERE bs2.id>=bs1.id AND bs2.type!='Inward' AND bs1.Productid=bs2.productid AND bs2.warehouseid=bs1.warehouseid)
)
END AS closingStock
INTO oldClosingStock,newClosingStock
FROM backfill_closing_stock bs1 WHERE bs1.entryid=entryid1;
IF newClosingStock>=0 AND newClosingStock<>oldClosingStock THEN
UPDATE inward_outward_entries SET closingStock = newClosingStock WHERE entryid=entryid1;
END IF;
END LOOP;
CLOSE cur;
TRUNCATE all_inventory_table;
INSERT INTO all_inventory_table SELECT * FROM all_inventory;
SET autocommit=1;
END;
I have two commands in line #13 and #14 resp. for truncating a table and inserting record to it for debugging purpose. I can see table getting truncated instantly but record with text "Procedure Started " is getting inserted to temp table only after 90 sec of execution. Not Sure where it is going wrong
Below is snippet from my logs
2022-01-27 09:49:33,165 INFO [Async-1] com.ec.application.service.AsyncServiceInventory: Before Procedure Start -2022-01-27T10:12:58.616
This shows that procedure was triggered from spring at 10:12:58
But when I check temp table, I do not see any record getting inserted for next 90 seconds. After that, say at 10:14:30, I see a record getting inserted with past time "Procedure Started 2022-01-27 10:12:58'"
I am not sure whats happening for this 90 seconds. any suggestions?
TRUNCATE (and other DDL statements) interrupts commits -- at least before MySQL 8.0
autocommit=0 is error-prone -- if you forget to COMMIT later, bad things can happen.
So, don't bother with autocommit and get the TRUNCATEs over with. Then do
START TRANSACTION;
do all the rest of the work
COMMIT;
But for real performance gains, find a way to rewrite it so you don't need a cursor. Cursors are slow.

SQL Rollback not going back to last commit

I'm having problem with the following in SQL:
SELECT
*
FROM
department_dup
ORDER BY dept_no;
Then I ran this piece of code:
COMMIT;
UPDATE department_dup
SET
dept_no = 'd011',
dept_name = 'Quality Control'
;
ROLLBACK;
SELECT * from department_dup;
But the table is not going back to the last commit
Can anyone please tell me what's going wrong here? Thanks!
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
Option 1: Set autocomit off
SET autocommit = 0
OPtion 2: Use transaction boundaries.
START TRANSACTION;
UPDATE department_dup
SET
dept_no = 'd011',
dept_name = 'Quality Control'
;
ROLLBACK;

MySql Transaction Success Table Lock, Last Insert

I have a few questions I am unsure of and would be a great help if someone could help me
1) I want to return to my code that the user was successful inserted into the database it nor a fail. Would I do this along the lines of If Last_Insert_id is not null? return a message saying inserted.
2) Will last_isert_id be particular to the user inserted, i.e. one insert at a time. Do i need to do a lock to achieve this. I.e. if i had a profile table for instance and i got last_isert_id i could guarantee if many ppl are signing up at once that each id would be for each user. Or do i need to do table locking.
Any other feedback most welcome in terms of improvements
BEGIN
DECLARE _user_role_permission int;
DECLARE _user_id int;
DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK;
DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK;
SET _user_role_permission = (SELECT id FROM user_role_permission WHERE role_permission = in_role_permission);
START TRANSACTION;
INSERT INTO user_site(username, email, status, password, password_strategy, salt, requires_new_password, reset_token,
login_time, lock_status, login_ip, created_ip, activation_key, validation_key,
create_time, update_time)
VALUES(in_username, in_email, in_status, in_password,
in_password_strategy, in_salt, in_requires_new_password,
in_reset_token, in_login_time, in_lock_status, in_login_ip,
in_created_ip, in_activation_key, in_validation_key,
in_create_time, in_update_time);
SET _user_id = LAST_INSERT_ID();
INSERT INTO user(user_site_id) VALUES(_user_id);
INSERT INTO user_permission(user_id, permission_id)VALUES (_user_id,_user_role_permission);
COMMIT;
END
You can use SELECT ##warning_count; and SELECT ##error_count; after INSERT to see if it succeeded. I yes, it will return 0 rows.
last_insert_id() is limited to the current connection, so no matter how many concurrent connections are there to database, you will always get correct value.

MYSQL Procedures run, but return 0 rows affected

I call a procedure, it runs, and the console says "0 rows affected". Is this normal behavior for a MySQL procedure ?
The procedures are clearly doing what they should. One procedure has 2 insert statements, another has an insert and update statement, and I've seen the results with my own eyes. There are indeed rows being affected.
I'm not sure that I would use that result later on, but it seems like I'd want to get an accurate response from my DB whether or not anything was updated, especially when its expected.
Thoughts ?
MySQL 5.5 if it matters, and the procedures use transactions over auto-committed statements.
CREATE DEFINER=`root`#`localhost` PROCEDURE `create_issue`(user_id SMALLINT, title varchar(255), body LONGTEXT)
BEGIN
DECLARE MYUSERID SMALLINT;
DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN END;
START TRANSACTION;
INSERT INTO tracker.issue (user_id, title, body, creation_date, last_mod_date) values (user_id, title, body, CURDATE(), CURDATE());
UPDATE user_activity SET last_new_issue = CURDATE(), post_count = post_count + 1 WHERE user_activity.user_id = user_id;
COMMIT;
END
Edited to show the actual query. Also I've been searching and as best as I can tell this is a known issue over a year and a half old. So I suppose this one can be closed.
the "0 rows affected" response is for the last statement executed in the stored procedure.
usually i track the number of rows effected by manually counting them into session variables
DELIMITER $$
CREATE PROCEDURE `create_issue`(user_id SMALLINT, title varchar(255), body LONGTEXT)
BEGIN
DECLARE MYUSERID SMALLINT;
DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN END;
SET #inserted_rows = 0;
SET #updated_rows = 0;
START TRANSACTION;
INSERT INTO tracker.issue (user_id, title, body, creation_date, last_mod_date) values (user_id, title, body, CURDATE(), CURDATE());
SET #inserted_rows = ROW_COUNT() + #inserted_rows;
UPDATE user_activity SET last_new_issue = CURDATE(), post_count = post_count + 1 WHERE user_activity.user_id = user_id;
SET #updated_rows = ROW_COUNT() + #updated_rows;
COMMIT;
END
$$
the session variables can then be read after the SP was executed.
i am not sure if it is possible to override the response from the ROW_COUNT() function by setting a value to a variable,
I guess this is a reported bug. May be a good question for MySQL mailing list/forum. http://bugs.mysql.com/bug.php?id=44854
Something definitely isn't right. A sproc should still return the number of rows affected if there are multiple inserts occurring. I'm using the same version of MySQL and this works fine for me.
Are you sure you're not doing something like that
...SET col1='value1' AND col2='value2'...
instead of
...SET COL1='value1', col2='value2'...
Could you post your stored procedure?