combining two columns in to one using multiple line triggers in mysql - mysql

The following is my table schema:
+--------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+-------+
| LicenceID | int(11) | NO | PRI | NULL | |
| PassingRTO | varchar(4) | NO | | NULL | |
| DLNO | int(15) | YES | | NULL | |
| DateOfIssue | date | NO | | NULL | |
| DateOfExpiry | date | NO | | NULL | |
| COV | varchar(6) | NO | | NULL | |
| DateOfBirth | date | NO | | NULL | |
| BloodGroup | varchar(3) | YES | | NULL | |
| FullName | varchar(50) | NO | | NULL | |
| FathersName | varchar(50) | YES | | NULL | |
| Address | varchar(150) | NO | | NULL | |
| PinCode | int(6) | NO | | NULL | |
| IssuingAuth | int(7) | NO | | NULL | |
| IDIA | int(11) | YES | | NULL | |
| Valid | tinyint(4) | NO | | NULL | |
+--------------+--------------+------+-----+---------+-------+
What i want to do is When i insert a new row, i want my DLNO as PassingRTO+LicenceID and IDIA as PassingRTO+IssuingAuth.
I tried the same using -
create trigger insert_combined
after insert on LicencesDB
for each row
BEGIN
set new.IDIA = concat(new.PassingRTO, new.IssuingAuth);
set new.DLNO = concat(new.PassingRTO,new.LicenceID);
END;
but gives me an error as -
ERROR 1362 (HY000): Updating of NEW row is not allowed in after
trigger ERROR 1193 (HY000): Unknown system variable 'DLNO' ERROR 1064
(42000): You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
near 'END' at line 1
Now I have two questions can we do this using multiple lines in triggers? and can't we combine two columns at the time of creating the table itself? like col1 = col2 + col3?
Thanks in advance!

don;t forget to change the DELIMITER
DELIMITER $$
create trigger insert_combined
after insert on LicencesDB
for each row
BEGIN
set new.IDIA = concat(new.PassingRTO, new.IssuingAuth);
set new.DLNO = concat(new.PassingRTO,new.LicenceID);
END $$
DELIMITER ;

Please check this link on mysql triggers. When you want to make an update on the current row being entered you can't use the after clause.
check http://dev.mysql.com/doc/refman/5.1/en/create-trigger.html
When you want to use
SET NEW.col_name = value
in your trigger, please note that you CANNOT use this with the AFTER the action, and must use it BEFORE the action.
Therefore, this will work:
CREATE TRIGGER sdata_insert BEFORE INSERT ON `sometable`
FOR EACH ROW
BEGIN
SET NEW.guid = UUID();
END
;
And this will NOT work:
CREATE TRIGGER sdata_insert AFTER INSERT ON `sometable`
FOR EACH ROW
BEGIN
SET NEW.guid = UUID();
END
;
Also you can probably do it in the code itself before even entering into the database.

Related

Trigger with Ids other than OLD.id

I have a table like this:
user_oauth:
+----------------+-----------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+-----------------+------+-----+---------+----------------+
| id | int(8) unsigned | NO | PRI | NULL | auto_increment |
| user_id | int(8) unsigned | NO | MUL | NULL | |
| google_id | varchar(30) | YES | UNI | NULL | |
| facebook_id | varchar(30) | YES | UNI | NULL | |
| windowslive_id | varchar(30) | YES | UNI | NULL | |
+----------------+-----------------+------+-----+---------+----------------+
which contains id of 3 the tables user_facebook, user_google, user_windowslive,
Example for user_facebook:
+-----------+-----------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+-----------------+------+-----+---------+-------+
| id | varchar(30) | NO | PRI | NULL | |
| email | varchar(60) | NO | UNI | NULL | |
| firstname | varchar(30) | YES | | NULL | |
| lastname | varchar(30) | YES | | NULL | |
| link | varchar(100) | YES | | NULL | |
| locale | varchar(5) | YES | | NULL | |
| picture | varchar(200) | YES | | NULL | |
| verified | int(1) unsigned | NO | | NULL | |
+-----------+-----------------+------+-----+---------+-------+
I would like to make a TRIGGER ON DELETE on user_oauth, which will delete user_facebook row if facebook_id of user_oauth is filled.
So I tried:
DELIMITER $$
CREATE TRIGGER `user_oauth_delete` BEFORE DELETE ON `user_oauth`
FOR EACH ROW BEGIN
DELETE FROM user_facebook
WHERE user_facebook.id = user_oauth.facebook_id;
END
$$
DELIMITER ;
But I have the error message:
Unknown column 'user_oauth.facebook_id' in 'where clause'
How to do it?
Thanks.
Solution
I had misunderstanding the OLD statement, so I had to:
DELIMITER $$
CREATE TRIGGER `user_oauth_delete` BEFORE DELETE ON `user_oauth`
FOR EACH ROW BEGIN
DELETE FROM user_facebook
WHERE user_facebook.id = OLD.facebook_id;
END
$$
DELIMITER ;
Don't do this with triggers. Do this with cascading delete constraints.
alter table oath
add constraint fk_oath_facebook
foreign key (facebookid) references user_facebook(id)
on delete cascade;
The documentation does a pretty good job of explaining constraints and the cascading capabilities.
Other than what have been suggested, you can as well consider wrapping both the DML statement inside a transaction block in a stored procedure and pass the facebook_id as parameter to the procedure. Such that both operation will happen in same order. Something like
DELIMITER $$
CREATE PROCEDURE `transaction_sp` (#facebook_id varchar(50))
LANGUAGE SQL
DETERMINISTIC
SQL SECURITY DEFINER
BEGIN
DECLARE exit handler for sqlexception
BEGIN
-- ERROR
ROLLBACK;
END;
DECLARE exit handler for sqlwarning
BEGIN
-- WARNING
ROLLBACK;
END;
START TRANSACTION;
DELETE FROM user_facebook WHERE id = #facebook_id;
DELETE FROM `user_oauth` WHERE facebook_id = #facebook_id;
COMMIT;
END
$$

Creating a scheduled event in MySQL

I have a table names offer inside a MySQL database named fouras :
mysql> desc offer;
+------------------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------------------+---------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| description | varchar(5000) | NO | | NULL | |
| end_date | date | NO | | NULL | |
| is_accepted | bit(1) | NO | | NULL | |
| is_active | bit(1) | NO | | NULL | |
| is_draft | bit(1) | NO | | NULL | |
| is_processed | bit(1) | NO | | NULL | |
| is_removed | bit(1) | NO | | NULL | |
| max_reservation_number | int(11) | NO | | NULL | |
| promotion_first_param | varchar(255) | YES | | NULL | |
| promotion_product | varchar(255) | YES | | NULL | |
| promotion_second_param | varchar(255) | YES | | NULL | |
| promotion_type | varchar(255) | NO | | NULL | |
| publish_date | date | YES | | NULL | |
| remove_time_stamp | bigint(20) | YES | | NULL | |
| start_date | date | NO | | NULL | |
| title | varchar(255) | NO | | NULL | |
| views_number | int(11) | YES | | NULL | |
| local_business | bigint(20) | YES | MUL | NULL | |
+------------------------+---------------+------+-----+---------+----------------+
19 rows in set (0.00 sec)
Now, i want to periodically check if an offer has expired (end_date > today), for that i'm trying to use a MySQL scheduled event :
CREATE EVENT IF NOT EXISTS check_expired_offers
ON SCHEDULE EVERY 10 MINUTE
DO
BEGIN
DECLARE id INT;
DECLARE end_date DATE;
DECLARE offer_cursor CURSOR FOR SELECT id, end_date FROM fouras.offer;
OPEN offer_sursor;
offer_loop: LOOP
FETCH offer_cursor into id, end_date;
IF end_date < NOW() THEN
UPDATE fouras.offer set is_active = false;
END IF;
END LOOP
END;
But MySQL throws an error when i try to add this event:
Error Code: 1064. You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 5 0,127 sec
The error was about the delimiter, this tutorial helped me a lot
==========================
Found delimiter issue.
==========================
Here's the modified event:
DELIMITER $$
CREATE EVENT IF NOT EXISTS check_expired_offers
ON SCHEDULE EVERY 10 MINUTE
DO
BEGIN
DECLARE finished INTEGER DEFAULT 0;
DECLARE id INT;
DECLARE end_date DATE;
DECLARE offer_cursor CURSOR FOR SELECT id, end_date FROM fouras.offer;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;
OPEN offer_cursor;
offer_loop:
LOOP
FETCH offer_cursor into id, end_date;
IF finished = 1 THEN
LEAVE offer_loop;
END IF;
IF end_date < NOW() THEN
UPDATE fouras.offer set is_active = false;
END IF;
END LOOP ;
END $$
DELIMITER ;
Learn delimiters in MySQL
Note: I've also used a variable there named finished.
Where finished is a variable to indicate that the cursor has reached the end of the result set. Notice that the handler declaration must appear after variable and cursor declaration inside the stored programs.
The following diagram illustrates how MySQL cursor works.

Syntax error when preparing statements in stored procedure

In a game for Android users can login via Google+, Facebook, Twitter:
When the app connects to the MySQL/PHP backend it sends a list of the social ids and I store them as sid column in social table:
+--------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+---------------+------+-----+---------+-------+
| sid | varchar(180) | NO | PRI | NULL | |
| given | varchar(180) | NO | | NULL | |
| family | varchar(180) | YES | | NULL | |
| city | varchar(180) | YES | | NULL | |
| photo | varchar(1000) | YES | | NULL | |
| stamp | int(11) | NO | | NULL | |
| uid | int(11) | NO | | NULL | |
+--------+---------------+------+-----+---------+-------+
In the other table called users I keep autoincremented user ids as uid column and use it to track their games and achievements (column medals):
+---------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+----------------+
| uid | int(11) | NO | PRI | NULL | auto_increment |
| created | int(11) | NO | | NULL | |
| stamp | int(11) | NO | | NULL | |
| banned_until | datetime | YES | | NULL | |
| banned_reason | varchar(180) | YES | | NULL | |
| medals | int(11) | NO | | 0 | |
+---------------+--------------+------+-----+---------+----------------+
In my PHP login script I try to merge social accounts whenever possible -
by taking all received sids, finding the corresponding uids and then taking the lowest found uid and updating the records in the social table to use that lowest uid.
This works well in PHP, but now I am trying to move the merging functionality into a MySQL stored procedure with 2 prepared statements:
delimiter $$$
drop procedure if exists merge_users;
create procedure merge_users(IN in_sids varchar(255))
begin
declare sql_1 varchar(255);
declare sql_2 varchar(255);
declare out_uid varchar(255);
set sql_1 = concat('select min(uid) into out_uid from social where sid in (', in_sids, ')');
prepare sth_1 from sql_1;
execute sth_1;
deallocate prepare sth_1;
IF found_rows() > 0 THEN
set sql_2 = concat('update social set uid=', out_uid, ' where sid in (', in_sids, ')');
prepare sth_2 from sql_2;
execute sth_2;
deallocate prepare sth_2;
ELSE
insert into users(created, stamp, medals, green, red)
values (unix_timestamp(), unix_timestamp(), 0, 0, 0);
select last_insert_uid() into out_uid;
END IF;
select out_uid;
end
$$$
Unfortunately, the above code prints the syntax error message:
ERROR 1064 (42000): You have an error in your SQL syntax;
check the manual that
execute sth_1;
deallocate prepare sth_1;
IF foun' at line 8

ERROR 1054 (42S22) on trigger

So what I'm trying to do is build a web application for sports applications (specifically soccer). Right now, I'm having trouble with a trigger that is supposed to update the standings after a match score is recorded. In this example, I have a 'game' table, and a 'standings' table, like so.
mysql> describe game;
+-----------------+------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+------------+------+-----+---------+-------+
| sid | int(11) | NO | PRI | NULL | |
| fid | int(11) | NO | PRI | NULL | |
| lid | int(11) | NO | PRI | NULL | |
| htid | int(11) | NO | PRI | NULL | |
| atid | int(11) | NO | PRI | NULL | |
| date | date | NO | | NULL | |
| time | time | NO | | NULL | |
| h_g | int(11) | NO | | 0 | |
| a_g | int(11) | NO | | 0 | |
| has_been_played | tinyint(1) | NO | | 0 | |
+-----------------+------------+------+-----+---------+-------+
10 rows in set (0.00 sec)
mysql> describe standings;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| tid | int(11) | NO | PRI | NULL | |
| sid | int(11) | NO | PRI | NULL | |
| lid | int(11) | NO | PRI | NULL | |
| pld | int(11) | NO | | 0 | |
| pts | int(11) | NO | | 0 | |
| h_w | int(11) | NO | | 0 | |
| h_t | int(11) | NO | | 0 | |
| h_l | int(11) | NO | | 0 | |
| h_gf | int(11) | NO | | 0 | |
| h_ga | int(11) | NO | | 0 | |
| a_w | int(11) | NO | | 0 | |
| a_t | int(11) | NO | | 0 | |
| a_l | int(11) | NO | | 0 | |
| a_gf | int(11) | NO | | 0 | |
| a_ga | int(11) | NO | | 0 | |
+-------+---------+------+-----+---------+-------+
15 rows in set (0.00 sec)
Where h/atid (and tid), fid, sid, and lid are foreign keys to team, field, season, and league tables, respectively.
I want to create a trigger after a game is updated. The current design I am aiming for in this application is that when a "game" is inserted, it has not been "played" yet and when it is updated, then the score is recorded and then the game is then considered "played." So here's a section of the trigger:
CREATE TRIGGER `update_standing` AFTER UPDATE ON `game`
FOR EACH ROW
BEGIN
# If a score has been recorded already, we'll reverse the old score
# before updating the new score.
IF OLD.has_been_played THEN
# The Home Team previously recorded a win
IF OLD.h_g > OLD.a_g THEN
# Home win
UPDATE standings
SET pld = pld - 1,
h_w = h_w - 1,
pts = pts - 3,
h_gf = h_gf - OLD.h_g,
h_ga = h_ga - OLD.a_g
WHERE tid = OLD.htid
AND sid = OLD.sid
AND lid = OLD.lid;
# Away loss
UPDATE standings
SET pld = pld - 1,
a_l = a_l - 1,
a_gf = a_gf - OLD.a_g,
a_ga = a_ga - OLD.h_g
WHERE tid = OLD.atid
AND sid = OLD.sid
AND lid = OLD.lid;
ENDIF;
ENDIF;
END;
And for some reason, I'm getting these errors, and I'm not sure why.
mysql> source new_trigger_myfam.sql
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 18
ERROR 1054 (42S22): Unknown column 'OLD.atid' in 'where clause'
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'ENDIF' at line 1
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'ENDIF' at line 1
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'END' at line 1
Is there something visibly wrong with my syntax? I understand that I could do the two update queries in one using case/when/then. Essentially, in this snippet of the trigger, I am reversing a previous update, and then there's more code actually making the rest of the update happen. I'm pretty new to triggers, so help is always appreciated.
As per the documentation, the IF block terminator in MySQL is two words: END IF.
So I got it with the help of someone else.
I need to wrap a delimiter around the triggers because the first instance of ; means that the entire trigger is "ended", when obviously that is not what I want.
Really stupid if you ask me, but what can you do?

mysql after insert trigger which updates another table's column

i'm trying to write a trigger, I have following tables:
BookingRequest:
+-----------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+---------+------+-----+---------+----------------+
| idRequest | int(11) | NO | PRI | NULL | auto_increment |
| roomClass | int(11) | NO | | NULL | |
| inDate | date | NO | | NULL | |
| outDate | date | NO | | NULL | |
| numOfBeds | int(11) | NO | | NULL | |
| status | int(11) | NO | MUL | NULL | |
| idUser | int(11) | NO | MUL | NULL | |
+-----------+---------+------+-----+---------+----------------+
status table:
+------------+--------------------------------------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------------------------------------------+------+-----+---------+-------+
| idStatus | int(11) | NO | PRI | NULL | |
| nameStatus | enum('underConsideration','approved','rejected') | YES | | NULL | |
+------------+--------------------------------------------------+------+-----+---------+-------+
OccupiedRoom:
+--------------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+---------+------+-----+---------+----------------+
| idOccupation | int(11) | NO | PRI | NULL | auto_increment |
| idRoom | int(11) | NO | | NULL | |
| idRequest | int(11) | NO | | NULL | |
+--------------+---------+------+-----+---------+----------------+
i need a trigger which will change status in BookingReques to 1 if request with the same id is inserted into OccupiedRoom table, so i tried something like this
create trigger occupy_trig after insert on OccupiedRoom
for each row
begin
if BookingRequest.idRequest= NEW.idRequest
then
update BookingRequest
set status = '1';
where idRequest = NEW.idRequest;
end if;
END;
and it doesn't work, so any suggestions would be very appriciated
Try this:
DELIMITER $$
CREATE TRIGGER occupy_trig
AFTER INSERT ON `OccupiedRoom` FOR EACH ROW
begin
DECLARE id_exists Boolean;
-- Check BookingRequest table
SELECT 1
INTO #id_exists
FROM BookingRequest
WHERE BookingRequest.idRequest= NEW.idRequest;
IF #id_exists = 1
THEN
UPDATE BookingRequest
SET status = '1'
WHERE idRequest = NEW.idRequest;
END IF;
END;
$$
DELIMITER ;
With your requirements you don't need BEGIN END and IF with unnecessary SELECT in your trigger. So you can simplify it to this
CREATE TRIGGER occupy_trig AFTER INSERT ON occupiedroom
FOR EACH ROW
UPDATE BookingRequest
SET status = 1
WHERE idRequest = NEW.idRequest;
Maybe remove the semi-colon after set because now the where statement doesn't belong to the update statement. Also the idRequest could be a problem, better write BookingRequest.idRequest
DELIMITER //
CREATE TRIGGER contacts_after_insert
AFTER INSERT
ON contacts FOR EACH ROW
BEGIN
DECLARE vUser varchar(50);
-- Find username of person performing the INSERT into table
SELECT USER() INTO vUser;
-- Insert record into audit table
INSERT INTO contacts_audit
( contact_id,
deleted_date,
deleted_by)
VALUES
( NEW.contact_id,
SYSDATE(),
vUser );
END; //
DELIMITER ;