Optimize MySQL stored procedure that is blocking my back-end transactions - mysql

I have this simple stored procedure that executes once per day to update the "energy" of the users depending on how many materials they have. But this takes around 2 minutes to end and I am wondering if there is a better way to do it:
BEGIN
SET #energy_premium = 10;
SET #energy_free = 5;
UPDATE user
SET energy = #energy_premium
WHERE id IN (
SELECT fk_user
FROM material
GROUP BY fk_user
HAVING COUNT(fk_user)>=2 AND user.id = material.fk_user);
UPDATE user
SET energy = #energy_free
WHERE id IN (
SELECT fk_user
FROM material
GROUP BY fk_user
HAVING COUNT(fk_user)=1 AND user.id = material.fk_user);
END
Also, when this stored procedure is executing my back-end services can't make transactions to the database.

Test this:
BEGIN
SET #energy_premium = 10;
SET #energy_free = 5;
UPDATE user
JOIN ( SELECT fk_user, CASE COUNT(fk_user) WHEN 1
THEN #energy_free
ELSE #energy_premium
END energy
FROM material
GROUP BY fk_user ) mat ON user.id = mat.fk_user
SET user.energy = mat.energy;
END;

Related

MySQL - 3 updates need to be run 1 by 1 in Procedure

I have to run 3 updates in one procedure. My 3 separates update work well when I call them one bye one in php but I would like to have them regroup to perform only one call. My procedure is
create
definer = root#localhost procedure UpdateScorePredictionParticipant(IN predPts int(10), IN predId int(10), IN partId int(10))
BEGIN
UPDATE predictions
SET pred_pts = #predPts
WHERE pred_game_id = #predId and participant_id = #partId;
UPDATE ranking
SET pts_wildcard = (SELECT sum(pred_pts)
FROM predictions
INNER JOIN games g on predictions.pred_game_id = g.id
WHERE predictions.participant_id = #partId)
WHERE participant_id = #partId;
UPDATE ranking
SET total = (pts_wildcard+pts_conference+pts_division+pts_pred_sb+pts_sb)
WHERE participant_id = #partId;
END;
How could I perform those 3 updates 1 by 1 but in the same call.
Thanks,
As you are updating 3 different tables, you will need to do three separate updates. However if you put them in a procedure, you only need call the procedure from the PHP code.
To fix the procedure code, remove the # from the used parameter names.
It is also a good practice to prefix the parameters, so they will not mix up with the column names.
Last thing you can do, is to use a transaction inside the procedure, so all updates complete or if some of them fail, none of them gets done. This ensures the atomicity in the operation.
create procedure UpdateScorePredictionParticipant(
in_predPts int,
in_predId int,
in_partId int
)
BEGIN
START TRANSACTION;
UPDATE predictions
SET pred_pts = in_predPts
WHERE pred_game_id = in_predId and participant_id = in_partId;
UPDATE ranking
SET pts_wildcard = (
SELECT sum(pred_pts)
FROM predictions
INNER JOIN games g on predictions.pred_game_id = g.id
WHERE predictions.participant_id = in_partId
)
WHERE participant_id = in_partId;
UPDATE ranking
SET total = (pts_wildcard+pts_conference+pts_division+pts_pred_sb+pts_sb)
WHERE participant_id = in_partId;
COMMIT;
END

DB for room entering limitation

I have making room that has limit of space. In this case suppose limit = 100.
And I have total that is when user join the room then total = total + 1.
If user join the room then check total <= limit.
If true then user can join the room if false can't.
So what I want to do for it in Mysql is
making before insert trigger for checking room is available before user join the room.
CREATE DEFINER = CURRENT_USER TRIGGER `relay_novel`.`RoomJoinedUsers_BEFORE_INSERT` BEFORE INSERT ON `RoomJoinedUsers` FOR EACH ROW
BEGIN
SET #total = 0;
SET #limit = 0;
SELECT #total := total FROM roomjoinedusersInfo WHERE roomId = NEW.roomId;
SELECT #limit := limit FROM rooms WHERE id = NEW.roomId;
IF (#total > #limit) {
// DON'T DO INSERT (HOW?)
}
END
My question is
How to do not inserting data into the database in before insert trigger?
Is there are better way to do it?
Please let me know if you need more info.
Thanks.

MySQL procedure returning wrong value (INSERT SELECT confronting)

I'm completely new to MySQL, and have been bumping with some errors, but always I do find solutions, except for this one I can't understand how to get around it.
The following MySQL Procedure returns me a value if variable "ue" is 1 or 0 (a bunch of exists validation). The validation part (SET ue = EXISTS...) works without the rest of the code, as it should, the problem is not there. But when I do execute the command INSERT INTO SELECT, it does not work, it always return 0 as response, when it should be 1. These two lines are getting in confrontation with each other.
INSERT INTO meetup_participation SELECT user_id, event_id FROM DUAL WHERE ue=1;
SELECT ue AS response;
The procedure should add 'user id' and 'event id' into meetup_participation, and then update the row at 'users' corresponding to the user with that 'user id' to increment the 'events participated'. And it also UPDATE to increment the participation in the event with this 'event id'.
I am using the SET ue to validate things like, if user exists, if event does exists, if date of event is still valid, and if user is not already in this table. So I am passing this value as a boolean to INSERT INTO meetup_participation [...] WHERE ue = 1. After that, I do SELECT ue to inform validation returned true and procedure executed without problems.
Here is the full procedure.
CREATE DEFINER=`user`#`localhost` PROCEDURE `join_event`(IN `user_id` BIGINT(64), IN `event_id` INT) NOT DETERMINISTIC MODIFIES SQL DATA SQL SECURITY DEFINER
begin
DECLARE ue INT;
SET ue = EXISTS(SELECT 1 FROM users WHERE fb_uid=user_id) AND EXISTS(SELECT 1 FROM meetup WHERE meet_id=event_id) AND EXISTS(SELECT 1 FROM meetup WHERE date > NOW() AND meet_id = event_id) AND EXISTS(SELECT 1 FROM meetup WHERE meet_id = event_id AND participants <= max_participants) AND NOT EXISTS(SELECT 1 FROM meetup_participation WHERE fb_uid = user_id AND meet_id = event_id);
INSERT INTO meetup_participation SELECT user_id, event_id FROM DUAL WHERE ue=1;
UPDATE users SET events_participated = events_participated + 1 WHERE fb_uid=user_id AND ue=1;
UPDATE meetup SET participants = participants + 1 WHERE meet_id=event_id AND ue=1;
SELECT ue AS response;
end
Thanks in advance.
The INSERT statement is executed separately from the SET ue =... statement. I'm not sure what you are trying to accomplish, but the code makes no sense.
If you want to add records to meetup_participation based on the EXISTS tests applied to each record in the users table, you would need to apply the tests to each record in your SELECT statement as part of the INSERT.
There are also numerous syntax/grammar issues in the code as shown.
If you could provide an explanation of what you are trying to accomplish with the procedure, that might allow someone to suggest the right way to code the procedure.
Selecting ue will not tell you if the procedure completed without error. You should research mysql transactions and mysql error handling. http://www.mysqltutorial.org/mysql-error-handling-in-stored-procedures/ is a good starting point.
You might end up with something like this
drop procedure if exists p;
delimiter //
CREATE DEFINER=`root`#`localhost` PROCEDURE `p`(
IN `inue` int,
IN `user_id` BIGINT(64),
IN `event_id` INT
)
LANGUAGE SQL
NOT DETERMINISTIC
MODIFIES SQL DATA
SQL SECURITY DEFINER
COMMENT ''
begin
DECLARE ue INT;
declare exit handler for sqlexception
begin
rollback;
insert into errors (msg) select concat('error ' ,inue,',',user_id,',',event_id);
end;
set autocommit = 0;
#set ue = inue;
SET ue = EXISTS(SELECT 1 FROM users WHERE fb_uid=user_id)
AND EXISTS(SELECT 1 FROM meetup WHERE meet_id=event_id)
#AND EXISTS(SELECT 1 FROM meetup WHERE dt > NOW() AND meet_id = event_id)
AND EXISTS(SELECT 1 FROM meetup WHERE meet_id = event_id AND ifnull(participants,0) <= max_participants)
AND NOT EXISTS(SELECT 1 FROM meetup_participation WHERE fb_uid = user_id AND meet_id = event_id)
;
select ue;
if ue = 1 then
start transaction;
INSERT INTO meetup_participation SELECT user_id, event_id,user_id, event_id;
UPDATE users SET events_participated = ifnull(events_participated,0) + 1 WHERE fb_uid=user_id = user_id;
UPDATE meetup SET participants = ifnull(participants,0) + 1 WHERE meet_id = event_id ;
commit;
end if;
SELECT ue AS response;
end //
The error table looks like this
CREATE TABLE `errors` (
`msg` varchar(2000) DEFAULT NULL,
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1
Note I am not suggesting this is a solution appropriate to your site , you need to do the research and figure out what is best for you.

Updating multiple tables using single trigger

I am having trouble with updating 2 tables with one trigger, its giving me an error near "New.userid"
CREATE TRIGGER userDownloads AFTER INSERT ON Downloads
FOR EACH ROW
UPDATE Project SET PROJECT_DOWNLOADS = PROJECT_DOWNLOADS + 1 WHERE PROJECTID = NEW.ProjectID,
UPDATE User SET NO_OF_DOWNLOADS = NO_OF_DOWNLOADS + 1 WHERE USERID = NEW.UserID;
You have to write your trigger this way:
DELIMITER //
CREATE TRIGGER userDownloads AFTER INSERT ON Downloads
FOR EACH ROW
BEGIN
UPDATE Project SET PROJECT_DOWNLOADS = PROJECT_DOWNLOADS + 1 WHERE PROJECTID = NEW.ProjectID;
UPDATE User SET NO_OF_DOWNLOADS = NO_OF_DOWNLOADS + 1 WHERE USERID = NEW.UserID;
END//
Please see it working here.
But are you sure you need a trigger? Why don't you just do a COUNT on your Downloads table?
SELECT ProjectID, COUNT(*) AS PROJECT_DOWNLOADS
FROM Downloads
GROUP BY ProjectID;
SELECT UserID, COUNT(*) AS NO_OF_DOWNLOADS
FROM Downloads
GROUP BY UserID;

mysql update column then select updated value

I have a table like this
tbl_user
id
user_id
amount
first i want to update a row based on id
$amount = 123; // dyanamic value
$sql = "UPDATE tbl_user SET amount=amount-'$amount' WHERE id='$id' LIMIT 1 ";
now i want to get updated value of amount column i have applied this sql
$sql = "SELECT amount FROM tbl_user WHERE id='$id' LIMIT 1 ";
my question is can i combine both of above sql or any single query to achieve above task?
The best you could imitate is to use two lines of queries, probably using a variable like:
UPDATE tbl_user SET
amount = #amount := amount-'$amount'
WHERE id='$id' LIMIT 1;
SELECT #amount;
The best you could do then is to create a Stored Procedure like:
DELIMITER //
CREATE PROCEDURE `return_amount` ()
BEGIN
UPDATE tbl_user SET
amount = #amount := amount-'$amount'
WHERE id='$id' LIMIT 1;
SELECT #amount;
END //
And then call Stored Procedure in your PHP.
Note: PostgreSQL has this kind of option using RETURNING statement that would look like this:
UPDATE tbl_user SET amount=amount-'$amount'
WHERE id='$id' LIMIT 1
RETURNING amount
See here
A function can do this easily. It sounds like you want to limit how many times your code connects to the database. With a stored function or procedure, you are only making one connection. Yes, the stored function has two queries inside it (update then select), but these are executed on the server side without stopping to do round trips to the client.
http://sqlfiddle.com/#!2/0e6a09/1/0
Here's my skeleton of your table:
CREATE TABLE tbl_user (
id VARCHAR(100) PRIMARY KEY,
user_id VARCHAR(100),
amount DECIMAL(17,4) );
INSERT INTO tbl_user VALUES ('1', 'John', '100.00');
And the proposed function:
CREATE FUNCTION incrementAmount
(p_id VARCHAR(100), p_amount DECIMAL(17,4))
RETURNS DECIMAL(17,4)
BEGIN
UPDATE tbl_user
SET amount = amount + p_amount
WHERE id = p_id;
RETURN (SELECT amount FROM tbl_user WHERE id = p_id);
END
//
Then you just run one query, a SELECT on the function you just created:
SELECT incrementAmount('1', 5.00)
The query result is:
105
It is not possible with a single query, but you can combine multiple commands into a script and execute them with a single request to the database server.
Run this script:
"UPDATE tbl_user SET amount=amount-'$amount' WHERE id='".$id."';SELECT amount FROM tbl_user WHERE id='".$id."'; "
Also, you might want to check whether $id is a number, as I do not see a protection against SQL injection inside your code. SQL injection is a serious threat, you would do better to prepare and protect yourself against it.
We can also use:
UPDATE tbl_user SET id = LAST_INSERT_ID(id), amount = 2.4,user_id=4 WHERE id = 123;
// SELECT
$id =SELECT LAST_INSERT_ID();
SELECT amount,user_id FROM tbl_user WHERE id = $id LIMIT 1
Here would be the procedure
CREATE PROCEDURE UpdateAndSelect
(
#amount MONEY,
#id INT
)
AS
BEGIN
UPDATE tbl_user
SET amount = #amount
WHERE id = #id
LIMIT 1
SELECT amount
FROM tbl_user
WHERE id = #id
LIMIT 1
END
GO
You would call this stored procedure by setting your variables (#amoutn and #id) and then calling:
exec UpdateAndSelect
Hope this helps solve your problem