Atomicity of the data in mysql - mysql

One of the client has asked us to write a store procedure in mysql, which states that one data should be accessed by only one resources (Even if their happens to be Multiple resources ready to read the data, whomsoever will come first will take the lock first and would change its flag so that not other resources should be able to take a lock on this data row in a table.
Store procedure is to be written for it, i believe it to be similar to bank transaction management, but i have no clue how to write a stored procedure for it, any help will be highly appreciated, Thanks well in advance.

Step : 1
CREATE TABLE `test_db`.`Jobs` (
`id` INT NOT NULL,
`JOB` VARCHAR(45) NOT NULL,
`status` VARCHAR(45) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `id_UNIQUE` (`id` ASC));
Step : 2
DELIMITER $$
create procedure aabraKaDaabra(IN ids INT)
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK;
DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK;
START TRANSACTION;
select id from Jobs where id=ids for update;
update Jobs set status = 'Submitted' where id=ids;
commit;
END$$;
Step : 3
select * from test_db.Jobs order by id desc;
Note:
Make sure that you have inserted a few of the value for the table.
Step : 4
call test_db.aabraKaDaabra(1);
This is what i was expecting and solved it , it worked like a charm

Related

How can auto-Incrementing be maintained when concurrent transactions occur on a compound key In MYSQL?

I recently encountered an error in my application with concurrent transactions. Previously, auto-incrementing for compound key was implemented using the application itself using PHP. However, as I mentioned, the id got duplicated, and all sorts of issues happened which I painstakingly fixed manually afterward.
Now I have read about related issues and found suggestions to use trigger.
So I am planning on implementing a trigger somewhat like this.
DELIMITER $$
CREATE TRIGGER auto_increment_my_table
BEFORE INSERT ON my_table FOR EACH ROW
BEGIN
SET NEW.id = SELECT MAX(id) + 1 FROM my_table WHERE type = NEW.type;
END $$
DELIMITER ;
But my doubt regarding concurrency still remains. Like what if this trigger was executed concurrently and both got the same MAX(id) when querying?
Is this the correct way to handle my issue or is there any better way?
An example - how to solve autoincrementing in compound index.
CREATE TABLE test ( id INT,
type VARCHAR(192),
value INT,
PRIMARY KEY (id, type) );
-- create additional service table which will help
CREATE TABLE test_sevice ( type VARCHAR(192),
id INT AUTO_INCREMENT,
PRIMARY KEY (type, id) ) ENGINE = MyISAM;
-- create trigger which wil generate id value for new row
CREATE TRIGGER tr_bi_test_autoincrement
BEFORE INSERT
ON test
FOR EACH ROW
BEGIN
INSERT INTO test_sevice (type) VALUES (NEW.type);
SET NEW.id = LAST_INSERT_ID();
END
db<>fiddle here
creating a service table just to auto increment a value seems less than ideal for me. – Mohamed Mufeed
This table is extremely tiny - you may delete all records except one per group with largest autoincremented value in this group anytime. – Akina
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=61f0dc36db25dd5f0cf4647d8970cdee
You may schedule excess rows removing (for example, daily) in service event procedure.
I have managed to solve this issue.
The answer was somewhat in the direction of Akina's Answer. But not quite exactly.
The way I solved it did indeed involved an additional table but not like the way He suggested.
I created an additional table to store meta data about transactions.
Eg: I had table_key like this
CREATE TABLE `journals` (
`id` bigint NOT NULL AUTO_INCREMENT,
`type` smallint NOT NULL DEFAULT '0',
`trans_no` bigint NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `transaction` (`type`,`trans_no`)
)
So I created a meta_journals table like this
CREATE TABLE `meta_journals` (
`type` smallint NOT NULL,
`next_trans_no` bigint NOT NULL,
PRIMARY KEY (`type`),
)
and seeded it with all the different types of journals and the next sequence number.
And whenever I insert a new transaction to the journals I made sure to increment the next_trans_no of the corresponding type in the meta_transactions table. This increment operation is issued inside the same database TRANSACTION, i.e. inside the BEGIN AND COMMIT
This allowed me to use the exclusive lock acquired by the UPDATE statement on the row of meta_journals table. So when two insert statement is issued for the journal concurrently, One had to wait until the lock acquired by the other transaction is released by COMMITing.

Rollback without START TRANSACTION statement is not working in MySQL Stored Procedure

I am using mysql v5.6.
After inserting duplicate record in users table through stored procedure It throws an exception and this is good but unable to rollback for table POSTS.
Here is SQL codes for SP :
DELIMITER //
CREATE PROCEDURE usp_add_user_tests
( IN `i_name` VARCHAR(50),
IN `i_email` VARCHAR(100),
IN `i_status` TINYINT(1) UNSIGNED,
OUT `p_sqlcode` INT(11) UNSIGNED,
OUT `p_status_message` VARCHAR(100)
)
MODIFIES SQL DATA
BEGIN
DECLARE duplicate_key CONDITION FOR 1062;
DECLARE EXIT HANDLER FOR duplicate_key
BEGIN SET p_sqlcode=1062; SET p_status_message='Duplicate key error';
ROLLBACK ;
END;
SET p_sqlcode=0;
INSERT INTO posts (title)
VALUES('test');
INSERT INTO users (name,email,status)
VALUES(i_name,i_email,i_status);
IF p_sqlcode<>0 THEN
SET p_status_message=CONCAT(p_status_message,' when inserting new user');
ELSE
SET p_status_message='Success';
END IF;
END //
DELIMITER ;
Is it possible to rollback for table posts without using Start Transaction statement.
I don't think you have actually started a "transaction". I am pretty sure that you need to implement one of the mechanisms listed here: https://dev.mysql.com/doc/refman/5.7/en/commit.html

MySQL 5.6 GET DIAGNOSTICS into log table and ROLLBACK previous DML

Apparently I cannot combine the GET DIAGNOSTICS and ROLLBACK statements inside a stored procedure: besides catching the error, I want to be able to reverse all the previous processed data, not just to stop the execution. Please find bellow my script:
Create the log table:
CREATE TABLE tbl_logs (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`txt` VARCHAR(100) NOT NULL,
PRIMARY KEY (`id`));
Create the stored procedure:
DELIMITER $$
CREATE PROCEDURE `test`()
BEGIN
DECLARE state CHAR(5) DEFAULT ’00000′;
DECLARE msg TEXT;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
begin
rollback;
GET DIAGNOSTICS CONDITION 1 state = RETURNED_SQLSTATE, msg = MESSAGE_TEXT;
insert into tbl_logs (txt) select concat(‘Error ‘,state,’: ‘,msg);
end;
start transaction;
insert into tbl_logs (txt) select state;
– drop table no_such_table;
insert into tbl_logs (txt) select ‘commit’;
commit;
END
call the procedure:
call test();
select * from tbl_logs;
=> check the table: 2 rows
00000
commit
Alter the procedure:
Take the comment out, making the drop table visible.
call the procedure:
call test();
select * from tbl_logs;
=> check the table: 2 new rows instead of just one, the last
00000
Error 42S02: Unknown table ‘no_such_table’
=> the handler catches the error and stops the execution, is just that doesn’t consider the rollback (no matter what/where other DML are previously made, they are committed)…
What am I doing wrong?
As documented under DROP TABLE Syntax:
Note 
DROP TABLE automatically commits the current active transaction, unless you use the TEMPORARY keyword.

how to create unique sequence in mysql

How to create unique sequence number in MySQL?
The scenario goes like, that in table1 the data say "A" in row1 can appear more than once.
So when it is first occurring a sequence no will be assigned to it, and the same will be assigned to it each time it appears again.
But the data "B" (say the next data entered) will have the next sequence no.
So i cant use auto_increment in this scenario. Say, i have to check the conditions c1 and c2 for this unique sequence no.
Looking for a stored procedure to implement this. Hope i am clear with my problem.
CREATE TABLE `seq` (
`n` BIGINT NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`n`)
);
DELIMITER $$
DROP PROCEDURE IF EXISTS getseq$$
CREATE FUNCTION getseq() RETURN BIGINT
BEGIN
DECLARE r BIGINT;
INSERT INTO `seq` (`n`) VALUES (NULL);
SELECT MAX(`n`) INTO r FROM `seq`;
COMMIT;
RETURN r;
END$$
DELIMITER ;
Concurrent transactions should be revised, but I think it would work, because indeed the mark of auto-increment is shared across transactions, but not the id resulting from the insert you made into the table.

MySQL trigger (replacing assertion) does not work

Can you help me with this problem? I have two tables in a MySQL database:
ServiceProvider(SPID, Name, ... etc.)
hasTag(SPID, TagID)
Each service provider is supposed to have at least one tag, and a maximum of five tags. The max-constraint is not a problem, but the min-constraint refuses to work properly. I first tried to implement this via assertions, but then I found out, that MySQL does not support assertions. Thus, I wrote the following trigger:
delimiter |
CREATE TRIGGER MinTags BEFORE INSERT
ON ServiceProvider
FOR EACH ROW BEGIN
IF EXISTS (SELECT SPID FROM ServiceProvider
WHERE NOT EXISTS (SELECT DISTINCT SPID FROM hasTag))
THEN INSERT INTO stop_action VALUES(1, 'Assert Failure');
END IF;
END;
|
delimiter ;
The insert of 'Assert Failure' into the stop_action table is only to create a constraint violation, so that the DB would abort the action.
Now, normally, when I insert any value into the ServiceProvider table, without inserting anything in to the hasTag table, I should get an error, right? But, somehow it doesn't work ... I can insert anything I want into the ServiceProvider table, without receiving any kind of error. Do you know, what is wrong with my code?
How about denormalising a tad:
ALTER TABLE ServiceProvider
ADD COLUMN TagID1 BIGINT UNSIGNED NOT NULL,
ADD COLUMN TagID2 BIGINT UNSIGNED NULL,
ADD COLUMN TagID3 BIGINT UNSIGNED NULL,
ADD COLUMN TagID4 BIGINT UNSIGNED NULL,
ADD COLUMN TagID5 BIGINT UNSIGNED NULL;
Include foreign key constraints, if appropriate.
As written, this trigger does not even use the values from the row to be inserted.
The syntax to get the value of the SPID column is:
NEW.SPID
Also, consider using the SIGNAL statement to raise an error.
If you want to use ASSERT in SQL, this post may help:
SQL Scripts - Does the equivalent of a #define exist?