MySQL Trigger syntax for ordering based on user id - mysql

I would like to create a trigger on the FRIENDS table so that friend pairs are ordered and there aren't duplicate entries with the same friends. The following trigger is throwing a syntax error at BEGIN IF (:new.USER1_ID > :new.USER2_ID) ... -- how is this trigger supposed to be formatted?
CREATE TABLE USERS (
USER_ID INTEGER NOT NULL,
FIRST_NAME VARCHAR(100) NOT NULL,
LAST_NAME VARCHAR(100) NOT NULL,
YEAR_OF_BIRTH INTEGER,
MONTH_OF_BIRTH INTEGER,
DAY_OF_BIRTH INTEGER,
SSN INTEGER,
EMAIL VARCHAR(100),
PHONE INTEGER,
PWD VARCHAR(100),
ADDRESS VARCHAR(500),
PRIMARY KEY(USER_ID)
);
CREATE TABLE FRIENDS (
USER1_ID INTEGER,
USER2_ID INTEGER,
FOREIGN KEY (USER1_ID) REFERENCES USERS(USER_ID) ON DELETE CASCADE,
FOREIGN KEY (USER2_ID) REFERENCES USERS(USER_ID) ON DELETE CASCADE,
PRIMARY KEY(USER1_ID, USER2_ID),
CHECK(USER1_ID!=USER2_ID)
);
CREATE TRIGGER FRIENDS_TRIGGER
BEFORE INSERT ON FRIENDS FOR EACH ROW
BEGIN
DECLARE TEMP INTEGER
BEGIN
IF (:new.USER1_ID > :new.USER2_ID) THEN
SET TEMP = :new.USER1_ID
SET :new.USER1_ID = :new.USER2_ID
SET :new.USER2_ID = TEMP
END IF
END
/

There are several problems. You need to terminate statements with semicolons, and so you also need to change the delimiter so the entire create trigger is treated as a single statement. :new should also be NEW. And you have an extra BEGIN thrown in there.
DELIMITER //
CREATE TRIGGER FRIENDS_TRIGGER
BEFORE INSERT ON FRIENDS
FOR EACH ROW
BEGIN
DECLARE TEMP INTEGER;
IF (NEW.USER1_ID > NEW.USER2_ID) THEN
SET TEMP = NEW.USER1_ID;
SET NEW.USER1_ID = NEW.USER2_ID;
SET NEW.USER2_ID = TEMP;
END IF;
END; //
DELIMITER ;

Related

i have an error in my sql syntax by Duplicate entry '' for key 'PRIMARY'

this is my query
CREATE TABLE buku(
kd_buku VARCHAR(5) PRIMARY KEY,
judul_buku VARCHAR(60),
kd_karang VARCHAR(5),
kd_terbit VARCHAR(5),
jumlah INT
);
INSERT INTO buku VALUES
('21','kalkulus','10','1',10),
('22','Metode Numerik','11','2',20),
('23','Sistem basis data','12','3',40),
('24','Pengantar Teknologi Informasi','12','3',41);
DELIMITER //
CREATE PROCEDURE ubahbuku
(IN kd_buku VARCHAR(5), IN judul_buku VARCHAR(60), IN kd_karang VARCHAR(5), IN kd_terbit VARCHAR(5), jumlah INT)
BEGIN
UPDATE buku SET kd_buku=#kd_buku, judul_buku=#judul_buku, kd_karang=#kd_karang, kd_terbit=#kd_terbit, jumlah=#jumlah;
END//
DELIMITER ;
CALL ubahbuku('24','sosiologi','10','1',30);
I have a problem when calling the ubahbuku function
Duplicate entry '' for key 'PRIMARY'
can you help me?
As has been already pointed out in the comments, your proc's update has no WHERE clause, so it will try to update every record in the table with the same incoming primary key value. Instead, add a WHERE clause to the update to target a single record by primary key kd_buku value. Try using this version of your stored procedure:
DELIMITER //
CREATE PROCEDURE ubahbuku
(IN p_kd_buku VARCHAR(5), IN p_judul_buku VARCHAR(60), IN p_kd_karang VARCHAR(5), IN p_kd_terbit VARCHAR(5), p_jumlah INT)
BEGIN
UPDATE buku
SET judul_buku = p_judul_buku, kd_karang = p_kd_karang, kd_terbit = p_kd_terbit, jumlah = p_jumlah
WHERE kd_buku = p_kd_buku;
END//
DELIMITER ;
You are trying to insert the value 24 for the primary key(kd_buku) through the procedure which is already inserted using this statement. This is the reason it is throwing a duplicate entry error.
INSERT INTO buku VALUES
('24','Pengantar Teknologi Informasi','12','3',41);

mysql: update a table's primary key which is referred by another table as foreign key

This is my DB structure
CREATE DATABASE Library;
CREATE TABLE Library.Book (
ID char(8),
name varchar(10) NOT NULL,
author varchar(10),
price float,
status int DEFAULT 0,
PRIMARY KEY (ID),
CHECK ( status >= 0 and status <= 1 ),
CHECK ( price >= 0 )
);
CREATE TABLE Library.Reader (
ID char(8),
name varchar(10),
age int,
address varchar(20),
PRIMARY KEY (ID)
);
CREATE TABLE Library.Borrow (
Book_ID char(8),
Reader_ID char(8),
Borrow_Date date,
Return_Date date,
CONSTRAINT FK_Book_ID FOREIGN KEY (Book_ID) REFERENCES Library.Book(ID),
CONSTRAINT FK_Reader_ID FOREIGN KEY (Reader_ID) REFERENCES Library.Reader(ID)
);
and I write a store procedure as below
DELIMITER //
CREATE PROCEDURE Library.ChangeBookID (IN OriginID char(8), IN ModifiedID char(8))
BEGIN
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION #处理异常
SET #STATUS = 1;
CREATE TABLE Library.book_copy
SELECT ID
FROM Library.Book;
START TRANSACTION;
UPDATE Library.book_copy
SET Library.book_copy.ID = ModifiedID
WHERE Library.book_copy.ID = OriginID;
if #STATUS = 0 THEN #status = 0 means no transaction
ALTER TABLE Library.Borrow
DROP FOREIGN KEY FK_Book_ID;
UPDATE Library.Borrow
SET Library.Borrow.Book_ID = ModifiedID
WHERE Library.Borrow.Book_ID = OriginID;
DROP TABLE Library.Book;
RENAME TABLE Library.Book_copy TO Library.Book;
ALTER TABLE Library.Borrow
ADD CONSTRAINT FK_Book_ID FOREIGN KEY (Book_ID) REFERENCES Library.Book(ID);
COMMIT;
ELSE
ROLLBACK ;
END IF;
END //
DELIMITER ;
What I want is that if the modified primary key value is conflicted with the rest of this table, the procedure will rollback, else it will commit the changes. However, it doesn't work as I expected.
I still do not know why the store procedure I wrote before goes wrong, but with Barmar's help, I get another way which is much cleaner to solve this question.
Now, the new store procedure is as below:
DELIMITER //
CREATE PROCEDURE Library.ChangeBookID (IN OriginID char(8), IN ModifiedID char(8))
BEGIN
START TRANSACTION;
IF NOT EXISTS(SELECT 1 FROM Library.book WHERE ID = ModifiedID) THEN
ALTER TABLE Library.Borrow
DROP FOREIGN KEY FK_Book_ID;
UPDATE Library.Book
SET Library.Book.ID = ModifiedID
WHERE Library.Book.ID = OriginID;
UPDATE Library.Borrow
SET Library.Borrow.Book_ID = ModifiedID
WHERE Library.Borrow.Book_ID = OriginID;
ALTER TABLE Library.Borrow
ADD CONSTRAINT FK_Book_ID FOREIGN KEY (Book_ID) REFERENCES Library.Book(ID);
COMMIT;
ELSE
ROLLBACK;
END IF;
END //
DELIMITER ;
Thank Barmar again and also thx4 tadman's help.

MySQL trigger to target an attribute of a column

I am working with an overlap super/subtype relationship dealing with person(s) in my DB. What I would like to do is have the overlapping subtypes insert new rows when the supertype gains a new row. I have attached my LRD to clarify the relationship. LRD
I would like to create a trigger that inserts new person rows into the correct subtype based on the attributes employee/user in the person table.
The code I have attempted so far gives me an error upon inserting rows into person noting "employee column does not exist". I would assume this is because this code is trying to use the if statement for the subtypes where it is in fact absent.
I would appreciate any feedback.
Table Details
CREATE TABLE PERSON
(person_id int(10) not null AUTO_INCREMENT,
first_name varchar(15) not null,
last_name varchar(15) not null,
employee char(1),
participant char(1),
CONSTRAINT person_pk PRIMARY KEY (person_id))
ENGINE=InnoDB;
CREATE TABLE EMPLOYEE
(eperson_id int(10) not null AUTO_INCREMENT,
enterprise_email varchar(30),
manager_id int(10),
CONSTRAINT employee_pk PRIMARY KEY (eperson_id),
CONSTRAINT employee_fk1 FOREIGN KEY(eperson_id) REFERENCES PERSON(person_id) ON update cascade,
CONSTRAINT employee_fk2 FOREIGN KEY(manager_id) REFERENCES EMPLOYEE(eperson_id) ON update cascade)
ENGINE=InnoDB;
CREATE TABLE PARTICIPANT
(pperson_id int(10) not null AUTO_INCREMENT,
city varchar(30),
state varchar(2),
zip int(5),
sign_up_date date,
termination_date date,
CONSTRAINT participant_pk PRIMARY KEY (pperson_id),
CONSTRAINT participant_fk FOREIGN KEY(pperson_id) REFERENCES PERSON(person_id) ON update cascade)
ENGINE=InnoDB;
Trigger Code
DELIMITER //
CREATE TRIGGER subtype_creator
AFTER INSERT ON PERSON
FOR EACH ROW
BEGIN
IF (employee = ‘e’ ) THEN
INSERT INTO EMPLOYEE
SET eperson_id = NEW.person_id,
last_name = NEW.last_name,
enterprise_email = NULL,
manager_id = NULL;
IF (participant = ‘p’ )THEN
INSERT INTO PARTICIPANT
SET pperson_id = NEW.person_id,
city=NULL,
state = NULL,
zip = NULL,
sign_up_date =NULL,
termination_date = NULL;
END IF;
END IF;
END//
DELIMITER ;
This may work for you.
First off, I think to have the AUTO_INCREMENT attribute on columns EMPLOYEE.eperson_id and PARTICIPANT.pperson_id is not needed.
Since both of those columns are FOREIGN KEYS and are referencing back to the person_id column of table PERSON, they need to have, and will be getting, their values from that column through the TRIGGER anyway so no need to autoincrement them in the tables.
So I would change that.
This TRIGGER should work with populating both tables EMPLOYEE and PARTICIPANT after INSERT on table PERSON:
DELIMITER //
CREATE TRIGGER subtype_creator
AFTER INSERT ON PERSON
FOR EACH ROW
BEGIN
INSERT INTO EMPLOYEE(eperson_id, enterprise_email, manager_id)
VALUES(NEW.person_id, NULL, NULL);
INSERT INTO PARTICIPANT(pperson_id, city, state, zip, sign_up_date, termination_date)
VALUES(NEW.person_id, NULL, NULL, NULL, NULL, NULL);
END//
DELIMITER ;
Hope this helps you.
I ended up figuring out two methods to solve my issue. I ended up altering my 'employee' and 'participant' into boolean/tinyint data types.
CREATE TABLE PERSON
(person_id int(10) not null AUTO_INCREMENT,
first_name varchar(15) not null,
last_name varchar(15) not null,
employee tinyint(1),
participant tinyint(1),
CONSTRAINT person_pk PRIMARY KEY (person_id))
ENGINE=InnoDB;
After that alteration I decided to try and break up the one trigger into two. This was successful.
Type 1
DELIMITER //
CREATE TRIGGER employee_creator
AFTER INSERT ON PERSON
FOR EACH ROW
BEGIN
IF (NEW.employee = 1 ) THEN
INSERT INTO EMPLOYEE
SET eperson_id = NEW.person_id,
last_name = NEW.last_name,
enterprise_email = NULL,
manager_id = NULL;
END IF;
END//
DELIMITER ;
DELIMITER //
CREATE TRIGGER participant_creator
AFTER INSERT ON PERSON
FOR EACH ROW
BEGIN
IF (NEW.participant =0 )THEN
INSERT INTO PARTICIPANT
SET pperson_id = NEW.person_id,
city=NULL,
state = NULL,
zip = NULL,
sign_up_date =NULL,
termination_date = NULL;
END IF;
END//
DELIMITER ;
After inplementing that first option I realized the ELSEIF would allow me to not split the two and create a single trigger.
Type 2
DELIMITER //
CREATE TRIGGER employee_creator
AFTER INSERT ON PERSON
FOR EACH ROW
BEGIN
IF (NEW.employee = 1 ) THEN
INSERT INTO EMPLOYEE
SET eperson_id = NEW.person_id,
last_name = NEW.last_name,
enterprise_email = NULL,
manager_id = NULL;
ELSEIF (NEW.participant =0 )THEN
INSERT INTO PARTICIPANT
SET pperson_id = NEW.person_id,
city=NULL,
state = NULL,
zip = NULL,
sign_up_date =NULL,
termination_date = NULL;
END IF;
END//
DELIMITER ;

mysql trigger to update one field of the newly added row

I have 'users' table with the following fields:-
user_id (int, auto increment PK)
encrypted_userid (varchar 50)
user_name (varchar 50)
user_location (varchar 50)
What I want to do is create a trigger so that when values are inserted into the users table into user_name and user_location, i want to populate the encrypted_userid field with an AES_ENCRYPTED value from user_id - e.g. AES_ENCRYPT(user_id,'MYAESKEY') but only for the newly INSERTed row
Is this possible in MySQL with some kind of trigger?
Thanks in advance.
So is there a solution to my problem that will not fail etc? using a trigger - all other solutions i tried from reading other sources just didn't work.
Well all solutions revolve around LAST_INSERT_ID() because it's the only multi-user safe way to obtain auto generated ID.
First possible way, if you're very fond of triggers, is to have a separate table for auto generated sequences. Your schema will look like this
CREATE TABLE users_seq (user_id INT NOT NULL PRIMARY KEY AUTO_INCREMENT);
CREATE TABLE users
(
user_id INT NOT NULL PRIMARY KEY DEFAULT 1,
encrypted_userid varchar(50),
user_name varchar(50),
user_location varchar(50),
FOREIGN KEY user_id_fk (user_id) REFERENCES users_seq (user_id)
);
And the trigger
DELIMITER //
CREATE TRIGGER useridinserttrigger
BEFORE INSERT ON users
FOR EACH ROW
BEGIN
INSERT INTO users_seq() VALUES();
SET NEW.user_id = LAST_INSERT_ID(),
NEW.encrypted_userid = AES_ENCRYPT(LAST_INSERT_ID(), 'MYAESKEY');
END//
DELIMITER ;
Second way is to leverage your existing schema but use a stored procedure
DELIMITER //
CREATE PROCEDURE insert_user(IN _name VARCHAR(50), IN _location VARCHAR(50))
BEGIN
DECLARE _id INT;
START TRANSACTION;
INSERT INTO users (user_name, user_location) VALUES(_name, _location);
SET _id = LAST_INSERT_ID();
UPDATE users
SET encrypted_userid = AES_ENCRYPT(_id, 'MYAESKEY')
WHERE user_id = _id;
COMMIT;
END//
DELIMITER ;
Sample usage:
CALL insert_user('johndoe', null);
I solved my problem using MySQL - Trigger for updating same table after insert (JCLG's entry)
CREATE TRIGGER `useridinserttrigger` BEFORE INSERT ON `users`
FOR EACH ROW BEGIN
DECLARE tmpid,tmpid2 INT(11);
SELECT user_id INTO tmpid FROM users ORDER BY user_id DESC LIMIT 1;
SET tmpid2=tmpid+1;
SET new.encrypted_userid=AES_ENCRYPT(tmpid2,'MYAESKEY');
END;

mySQL Stored Procedure - Cursor error

I'm trying to write a mySQL procedure with a cursor to calculate a fare. I pass in the stationid's then I figure out what zone they are in. The fare is a set value of $1 and an additional $0.20 for each zone traveled in.
The code I have so far runs but there is a problem with the cursor not fetching the values into the variables.
Any help would be greatly appreciated. Thanks
Tables:
DROP DATABASE IF EXISTS luasSystem;
CREATE DATABASE luasSystem;
USE luasSystem;
CREATE TABLE IF NOT EXISTS line
(
line_id INT NOT NULL AUTO_INCREMENT,
Line_colour CHAR(10) NOT NULL,
PRIMARY KEY (line_id)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS zone
(
zone_id INT NOT NULL AUTO_INCREMENT,
zone_name VARCHAR(20) NOT NULL,
line INT NOT NULL,
PRIMARY KEY (zone_id),
FOREIGN KEY (line) REFERENCES line(line_id) ON UPDATE CASCADE ON DELETE RESTRICT
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS station
(
station_id INT NOT NULL AUTO_INCREMENT,
station_name CHAR(20) NOT NULL,
service CHAR(20),
line INT NOT NULL,
zone INT NOT NULL,
PRIMARY KEY (station_id),
FOREIGN KEY (line) REFERENCES line(line_id) ON UPDATE CASCADE ON DELETE RESTRICT,
FOREIGN KEY (zone) REFERENCES zone(zone_id) ON UPDATE CASCADE ON DELETE RESTRICT
) ENGINE=InnoDB;
Stored Procedure:
DROP PROCEDURE IF EXISTS calculateFare;
DELIMITER //
CREATE PROCEDURE calculateFare
(
IN stationid1 INT, IN stationid2 INT
)
BEGIN
DECLARE zoneNum1 INT;
DECLARE zoneNum2 INT;
DECLARE num INT;
DECLARE fare DOUBLE;
DECLARE tableEnd BOOLEAN;
DECLARE zoneCur CURSOR FOR
SELECT zone, zone FROM station
WHERE station_name = stationid1 AND station_name = stationid2;
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET tableEnd = TRUE;
OPEN zoneCur;
the_loop: LOOP
FETCH zoneCur
INTO zoneNum1, zoneNum2;
IF tableEnd THEN
CLOSE zoneCur;
LEAVE the_loop;
END IF;
SET fare = 1;
SET num = 0;
IF zoneNum1 < zoneNum2 THEN
SET num = zoneNum2 - zoneNum1;
ELSEIF zoneNum1 > zoneNum2 THEN
SET num = zoneNum1 - zoneNum2;
END IF;
SET fare = (num * 0.20) + 1;
SELECT fare;
END LOOP the_loop;
END //
DELIMITER ;
CAll calculateFare(3,5);
Wouldn't this be easier of you used decimal values instead of integers? Suggest you look at : http://dev.mysql.com/doc/refman/5.0/en/fixed-point-types.html