MySql Transaction Success Table Lock, Last Insert - mysql

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.

Related

Stored Procedure ahowing 'lock wait timeout exceeded try restarting transaction in mysql stored procedure' error?How to handel it?

Hello guys i am trying to validate otp verification manually.for that i have written this stored procedure .When i started this code was working fine but after some time its start giving error.It was taking too long time to execute approx 50sec and after that it was giving the error lock wait timeout.So can anyone tell me why its giving such error and how to resolve it?
CREATE DEFINER=`xxxxx`#`xxxx` PROCEDURE `new_mobile_authentication`(
IN in_macID VARCHAR(500),IN in_otp INT(5),OUT in_msg VARCHAR(100))
BEGIN
DECLARE userCount INT(10);
DECLARE emailID VARCHAR(100);
DECLARE mobileNumber BIGINT(11);
DECLARE checkmatched INT(5);
DELETE FROM mob_user WHERE NOW()>end_time;
SELECT COUNT(*),email,mobile,otp into userCount,emailID,mobileNumber,checkmatched FROM mob_user WHERE mac_id=in_macID ;
SET #checkEmailPresent=(SELECT COUNT(*) FROM table A WHERE email_id=emailID);
IF(userCount!=0 AND #checkEmailPresent!=0)THEN
IF(checkmatched=in_otp)THEN
UPDATE table A SET auth='YES',mac_id=in_macID,mobile_num=mobileNumber WHERE email=emailID;
SET #affRow=(SELECT ROW_COUNT());
DELETE FROM mob_user WHERE mac_id=in_macID;
SELECT #affRow AS affRow,email FROM table A WHERE mac_id=in_macID;
ELSE
SELECT 'invalid otp' INTO in_msg;
END IF;
ELSEIF(userCount!=0 AND #checkEmailPresent=0)THEN
IF(checkmatched=in_otp)THEN
INSERT INTO table A(email,mobile_num,mac_id) VALUE (emailID,mobileNumber,in_macID,);
SET #affRow=(SELECT ROW_COUNT());
DELETE FROM mob_user WHERE mac_id=in_macID;
SELECT #affRow AS affRow,email FROM table A WHERE mac_id=in_macID;
ELSE
SELECT 'invalid otp' INTO in_msg;
END IF;
ELSE
SELECT 'session expired' INTO in_msg;
END IF;
END
Fix timeouts by adding indexes and/or reformulating queries.
mob_user needs INDEX(end_time) and INDEX(mac_id).
SELECT COUNT(*), this, that ... without a GROUP BY does not makes sense. Nor will it work right with a GROUP BY. What were you expecting??
SET #checkEmailPresent=(SELECT COUNT(*) FROM table A WHERE email_id=emailID) can be rewritten SELECT #checkEmailPresent := COUNT(*) FROM table A WHERE email_id=emailID). Note the :=. That table needs INDEX(email_id).
IF(checkmatched=in_otp)THEN does not make sense since checkmatched is nowhere set. No that SELECT does not set it.
What is ROW_COUNT()? I don't think it is a MySQL function.

MySQL stored procedure performance issue when using cursor and temporary table

I tried to replace heavy Java method which runs multiple requests to the DB with stored SQL procedure.
It's doing its work but I expected much higher performance improvement.
The logic of the procedure(as well as Java method):
Get list of IDs from table1(purpose)
Iterate the list and get average value of a field from table2(record) for each id
Return list
of pairs id/average_value
Are there any efficiency issues in the procedure?
DROP PROCEDURE IF EXISTS test1.getGeneralAverage;
CREATE DEFINER=root#localhost PROCEDURE getGeneralAverage()
BEGIN
DECLARE p_id BIGINT(20);
DECLARE exit_loop BOOLEAN;
DECLARE cur CURSOR FOR
SELECT purpose_id FROM purpose
WHERE purpose.type = 'GENERAL'
AND (SELECT COUNT(*) > 0 FROM record
WHERE record.purpose_id=purpose.purpose_id) is true;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET exit_loop = TRUE;
CREATE TEMPORARY TABLE IF NOT EXISTS general_average
(id BIGINT(20), average DOUBLE) ENGINE=memory;
TRUNCATE TABLE general_average;
OPEN cur;
average_loop: LOOP
FETCH cur INTO p_id;
INSERT INTO test1.general_average (id, average)
VALUES (p_id, (SELECT AVG(amount) FROM record
WHERE record.purpose_id=p_id));
IF exit_loop THEN
CLOSE cur;
LEAVE average_loop;
END IF;
END LOOP average_loop;
INSERT INTO test1.general_average (id, average)
VALUES (0,
(select avg(amount) from record where purpose_type='CUSTOM'));
SELECT * FROM general_average;
END
Several patterns to change...
Try to avoid CURSORs; usually the entire operation can be done with a single SQL statement. That will be much faster.
INSERT ... VALUES (0, ( SELECT ... ) ) --> INSERT ... SELECT 0, ...
Don't use a TEMP table when you can simply deliver the results. In your case, you may need a UNION ALL to deliver the two chunks at once.

How to transform/migrate a mysql trigger to a Sql Server Trigger

I did a trigger in mysql to shoot alerts always an input value was less than the set value. But now I need it is done in SQL SERVER.
I would be grateful if someone could help me transform mysql trigger to a SQL Server trigger.
Thanks to all at once.
My trigger is:
DELIMITER $$
create TRIGGER alert
AFTER INSERT ON records
FOR EACH ROW
begin
Set #comp=0;
Set #tempmax=0;
Set #tempmin=0;
select lim_inf_temp into #tempmin from sensores where idSensor=NEW.idSensor;
Set #maxidAlarme=0;
if (CAST(NEW.Temperatura AS UNSIGNED)<#tempmin) then
SELECT MAX(idAlarme) into #maxidAlarme FROM alarmes;
SET #maxidAlarme=#maxidAlarme+1;
INSERT INTO alarmes(idAlarme,descricao_alarme, idRegisto) VALUES (#maxidAlarme,"inserted below the normal temperature",New.idRegisto);
INSERT INTO sensores_tem_alarmes(idSensor,idAlarme,dataAlarme) VALUES (NEW.idSensor,#maxidAlarme,NOW());
set #comp=+1;
end if;
set #id_sensores_em_alerta=1;
SELECT MAX(id_sensores_em_alerta) into #id_sensores_em_alerta FROM sensores_em_alerta;
INSERT INTO sensores_em_alerta(id_sensores_em_alerta, idSensor, idAlarme, data_registo, numerosensoresdisparados) VALUES (id_sensores_em_alerta,NEW.idSensor, #maxidAlarme, NOW(), #comp);
end $$;
DELIMITER ;
I've tried to make the trigger in SQL Server, but as the script is different and I'm getting many difficulties to do the right way.
My attempt that was not going at all well:
CREATE TRIGGER Alert ON registos AFTER INSERT AS
BEGIN
DECLARE #comp decimal= 0
DECLARE #tempmax decimal= 0
DECLARE #tempmin decimal= 0
DECLARE #current_max_idAlarme int = (SELECT MAX(IdAlarme) FROM alarmes)
-- Insert into alarmes from the inserted rows if temperature less than tempmin
INSERT alarmes (IdAlarme, descricao_alarme, idRegisto)
SELECT
ROW_NUMBER() OVER (ORDER BY i.idRegisto) + #current_max_idAlarme,
'temp Error',
i.idRegisto
FROM
inserted AS i
WHERE
i.Temperatura < #tempmin
END
But dont do anything.
Dont create data on table alarmes :S
Does anyone could help me please. I would be eternally grateful.
Many Greetings and thank you all.
First of all, MSSQL doesn't have the option FOR EACH ROW, so it treats multiple inserted rows at once as a set. You will therefore have to insert the values into a table variable.
Unfortunately I do not know much MySQL actually, but I believe this is a starting point?
CREATE TRIGGER ALERT
ON records
AFTER INSERT
AS
BEGIN
DECLARE #comp INT;
DECLARE #tempmax INT;
DECLARE TABLE #tempmin (tempmin INT);
INSERT INTO #tempmin
SELECT s.lim_inf_temp FROM sensores s WHERE s.idSensor IN (inserted.idSensor);
--rest of the code
I'm going to post this code against my better judgement - redesign the tables is better than this hack.
This uses a ROW_number() to virtualise a surrogate identity key for the alarmes table. This is a 'bad plan' (tm).
Also the answer is partial - it doesn't do everything your question asked for -- I hope it gets your further along the road. Use it as a guide for how to interact with the virtual INSERTED table. Good luck
CREATE TRIGGER Alert ON records AFTER INSERT AS
BEGIN
DECLARE #comp INT = 0
DECLARE #tempmax INT = 0
DECLARE #tempmin INT = 0
-- get the max current id.
-- note that this is EXTREMELY unsafe as if two pieces of code are executing
-- at the same time then you *will* end up with key conflicts.
-- you could use SERIALIZABLE.... but better would be to redisn the schema
DECLARE #current_max_idAlarme = (SELECT MAX(IdAlarme) FROM alarmes)
-- Insert into alarmes from the inserted rows if temperature less than tempmin
INSERT alarmes (IdAlarme, descricao_alarme, idRegisto)
SELECT
ROW_NUMBER() OVER (ORDER BY i.idRegisto) + #current_max_idAlarme,
'temp Error',
i.idRegisto
FROM
inserted AS i
WHERE
i.Temperatura < #tempmin
END

Why is the SQL transaction not being rolled back?

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.

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?