DB for room entering limitation - mysql

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.

Related

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

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;

Event Scheduler in Mysql not running

I have written event scheduler in MySQL. After executing, this event is not working at all. The task which is written is not happening. Below is my event scheduler,
DELIMITER $$
CREATE EVENT `Untravelled_Deduction` ON SCHEDULE EVERY 1 DAY STARTS '2016-04-01 06:42:00' ON COMPLETION NOT PRESERVE ENABLE DO BEGIN
DECLARE UserId INT;
DECLARE v_finished INT DEFAULT 0;
DECLARE GetDate DATE DEFAULT DATE(NOW());
/*get each user who's account is activated and not swiped for the given date*/
DECLARE UnTravelled CURSOR FOR
SELECT DISTINCT U.user_id
FROM `um.user` U
INNER JOIN `um.user_ps.pass` UP ON UP.user_id=U.user_id
INNER JOIN `ps.pass` P ON P.pass_id=UP.pass_id AND P.status_id=4
INNER JOIN `um.user_trs.tariff` UT ON UT.user_id = U.user_id
WHERE U.is_verified_email=1 AND U.is_active=1
AND UT.user_tariff_id = (
/*check user available_journeys journeys is available or not*/
SELECT MAX(UT2.user_tariff_id) FROM `um.user_trs.tariff` UT2 WHERE UT2.user_id = UT.user_id
AND UT2.available_journeys>0 AND UT2.current_balance>0 AND UT2.end_date>=GetDate
);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_finished=1;
/*Match th date with holiday list*/
SET #HolidayCount=(SELECT COUNT(*) FROM `holiday_list` WHERE DATE(holiday_date)=GetDate);
/*Match date for saturday and sunday*/
IF DAYOFWEEK(GetDate)!=1 AND DAYOFWEEK(GetDate)!=7 AND #HolidayCount=0 THEN
OPEN UnTravelled;
get_userid:LOOP
FETCH UnTravelled INTO UserId;
IF v_finished=1 THEN
LEAVE get_userid;
END IF;
/*Find user is registered for two way or one way, if #UserRouteCount=2 i.e. two way, #UserRouteCount=1 i.e. one way, */
SET #UserRouteCount = (SELECT COUNT(*) FROM `um.user_rm.route` WHERE user_id = UserId);
/*Get user swipe count to check how many times he swiped for the day. if only one and he is one way then deduct only once*/
SET #UserSwipeCount = (SELECT COUNT(*) FROM `ts.swipe_information` WHERE user_id = UserId AND DATE(swipe_in_time)=GetDate);
/*if user is two way and swiped only once for the day then deduct only once*/
IF (#UserRouteCount=2 AND #UserSwipeCount=1) THEN
SET #RouteCount=1;
ELSE
SET #RouteCount=#UserRouteCount;
END IF;
SET #i=1;
/*Get ticket details on this date for the user*/
SET #TicketCont= (SELECT COUNT(ticket_id) FROM `ts.ticket` WHERE DATE(`issued_on`)=GetDate AND user_id=UserId);
SET #IsInsert=0;
/*Check if any ticket is issued for the user on this date. if not he not travelled and go ahead to deduct*/
IF (#TicketCont=0) THEN
SET #IsInsert=1;
END IF;
/*check if ticket issued once, if he is two way user then decuct once*/
IF (#TicketCont=1 AND #UserRouteCount=2) THEN
SET #IsInsert=1;
END IF;
WHILE #i <= #RouteCount DO
IF (#IsInsert=1) THEN
/*Generate ticket if not exist for given date*/
/*get user current tariff plan*/
SET #UserTariffId = (SELECT user_tariff_id FROM `um.user_trs.tariff` WHERE user_id =UserId AND expired_on >= GetDate AND available_journeys > 0 ORDER BY user_tariff_id LIMIT 1);
IF(#UserTariffId IS NOT NULL)
THEN
SET #PerJourneyCost = (SELECT per_journey_cost FROM `um.user_trs.tariff` WHERE user_tariff_id=#UserTariffId );
SET #TariffCurrentBalance = (SELECT current_balance FROM `um.user_trs.tariff` WHERE user_tariff_id=#UserTariffId );
INSERT INTO `ts.ticket`(user_id,ticket_type_id,ticket_number,issued_on,
amount_charged,user_tariff_id,opening_balance,is_untravelled) VALUES
(UserId,1,'',UTC_TIMESTAMP(),#PerJourneyCost,#UserTariffId, #TariffCurrentBalance,1);
IF #PerJourneyCost IS NOT NULL THEN
/*Update user current tariff balance and number of journeys*/
UPDATE `um.user_trs.tariff` SET current_balance=(current_balance-#PerJourneyCost),
available_journeys =(available_journeys-1) WHERE user_tariff_id = #UserTariffId;
END IF;
END IF;
END IF;
SET #i=#i+1;
END WHILE;
/*Update user balance details and update Updated date in User table*/
UPDATE `um.user` SET updated_on=UTC_TIMESTAMP() WHERE user_id = UserId;
END LOOP get_userid;
CLOSE UnTravelled;
END IF;
END$$
DELIMITER ;
Couldn't rectify the issue, Please suggest any improvements.
Regards
Sangeetha
Creating event is not enough.
Make sure that scheduler is actually enabled:
MariaDB [(none)]> show global variables like 'event_scheduler';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| event_scheduler | OFF |
+-----------------+-------+
1 row in set (0.00 sec)
If not, try to activate it at runtime:
SET GLOBAL event_scheduler = ON;
or
SET ##global.event_scheduler = ON;
Or in /etc/my.conf (or wherever your config is located), and restart server:
event_scheduler=ON
Official docs:
Event Scheduler Configuration
Event scheduler options

Update a second table, using trigger

I'm trying to create a simple trigger, but, i can't set the media value on prato if the value comes from #total or #sum.
I've already tested to substitute them for "1" or "1+1", to see if i wasn't even updating correctly or the problem was the operation itself.
DELIMITER $$
CREATE
TRIGGER ratings_prato BEFORE INSERT ON ratings
FOR EACH ROW BEGIN
SET #total = #total + 1;
SET #sum = NEW.stars + #sum;
UPDATE prato p SET p.media = #sum/#total WHERE p.id = 1;
END;
$$
DELIMITER ;
Any ideias?
I've completed mislead the FOR EACH ROW on trigger. I thought, every time it was updated, It would run trough all rows. But, instead of it, it executes for each row it was updated, which, make things possible.
This was the last result of my trigger, inserting on ratings, and updating on food(which was prato)
DELIMITER $$
CREATE
TRIGGER inserting_dishes_rating AFTER INSERT ON ratings
FOR EACH ROW BEGIN
IF(NEW.food_id is not null) THEN
SET #media = (SELECT SUM(stars) FROM ratings WHERE food_id = NEW.food_id) / (SELECT COUNT(*) FROM ratings WHERE food_id = NEW.food_id);
      UPDATE foods f SET f.rate = #media WHERE f.id = NEW.food_id;
    ELSE
  SET #media = (SELECT SUM(stars) FROM ratings WHERE restaurant_id = NEW.restaurant_id) / (SELECT COUNT(*) FROM ratings WHERE restaurant_id = NEW.restaurant_id);  
UPDATE restaurants r SET r.rate = #media WHERE r.id = NEW.restaurant_id;
END IF;
END

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

Finding min and max value of the table in a constant time

I have a table which contains relative large data,
so that it takes too long for the statements below:
SELECT MIN(column) FROM table WHERE ...
SELECT MAX(column) FROM table WHERE ...
I tried index the column, but the performance still does not suffice my need.
I also thought of caching min and max value in another table by using trigger or event.
But my MySQL version is 5.0.51a which requires SUPER privilege for trigger and does not support event.
It is IMPOSSIBLE for me to have SUPER privilege or to upgrade MySQL.
(If possible, then no need to ask!)
How to solve this problem just inside MySQL?
That is, without the help of OS.
If your column is indexed, you should find min(column) near instantly, because that is the first value MySQL will find.
Same goes for max(column) on an indexed column.
If you cannot add an index for some reason the following triggers will cache the MIN and MAX value in a separate table.
Note that TRUE = 1 and FALSE = 0.
DELIMITER $$
CREATE TRIGGER ai_table1_each AFTER INSERT ON table1 FOR EACH ROW
BEGIN
UPDATE db_info i
SET i.minimum = LEAST(i.minimum, NEW.col)
,i.maximum = GREATEST(i.maximum, NEW.col)
,i.min_count = (i.min_count * (new.col < i.minumum))
+ (i.minimum = new.col) + (i.minimum < new.col)
,i.max_count = (i.max_count * (new.col > i.maximum))
+ (i.maximum = new.col) + (new.col > i.maximum)
WHERE i.tablename = 'table1';
END $$
CREATE TRIGGER ad_table1_each AFTER DELETE ON table1 FOR EACH ROW
BEGIN
DECLARE new_min_count INTEGER;
DECLARE new_max_count INTEGER;
UPDATE db_info i
SET i.min_count = i.min_count - (i.minimum = old.col)
,i.max_count = i.max_count - (i.maximum = old.col)
WHERE i.tablename = 'table1';
SELECT i.min_count INTO new_min_count, i.max_count INTO new_max_count
FROM db_info i
WHERE i.tablename = 'table1';
IF new_max_count = 0 THEN
UPDATE db_info i
CROSS JOIN (SELECT MAX(col) as new_max FROM table1) m
SET i.max_count = 1
,i.maximum = m.new_max;
END IF;
IF new_min_count = 0 THEN
UPDATE db_info i
CROSS JOIN (SELECT MIN(col) as new_min FROM table1) m
SET i.min_count = 1
,i.minimum = m.new_min;
END IF;
END $$
DELIMITER ;
The after update trigger will be some mix of the insert and delete triggers.