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;
Related
Is there a way to set at the time of creating a table a custom ID with some character as prefix and the rest are numbers which is auto incremented so that the first time a record is inserted the ID will be "UID0000001" and the second time a record is inserted the ID will be "UID0000002" and so on automatically in MySQL.
You could do it in the database via a trigger. What you would need to do is to use an auto_increment-column and get the value of it in the BEFORE-trigger:
delimiter $$
drop table thetable
$$
create table thetable (
id int auto_increment,
id_text varchar(20),
another varchar(20),
primary key(id),
unique index(id_text)
)
$$
CREATE TRIGGER thetable_ibefore
BEFORE INSERT ON thetable
FOR EACH ROW
BEGIN
DECLARE v_id int;
select auto_increment into v_id
from information_schema.tables
where table_schema = database() and table_name = 'thetable';
SET NEW.id_text = concat('UID',substr(concat('000000', v_id), -7));
END;
$$
insert into thetable (another) values ('ABC')
$$
select * from thetable
$$
Assuming the prefix would always be UID, then you should just maintain an auto increment id column and then build the UIDxxxxx value when you query or in your presentation layer. If the prefix could vary, then you would need to state the rules if you want a concrete answer.
For example:
CREATE TABLE yourTable (
id int NOT NULL AUTO_INCREMENT,
some_col varchar(255) NOT NULL,
-- other columns here
PRIMARY KEY (id)
);
SELECT CONCAT('UID', LPAD(id, 7, '0')) AS uid
FROM yourTable;
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 ;
In my MySQL Database, I have a table with a composite primary key where the ID is not in auto_increment mode. Something like this :
CREATE TABLE table_a (
fk_table_b INT UNSIGNED NOT NULL,
id INT UNSIGNED,
label VARCHAR(80) NOT NULL,
PRIMARY KEY (fk_table_b, id),
FOREIGN KEY fk_table_b
REFERENCES table_b(id)
);
To increment the ID in function of the foreign key, I made a trigger like this :
DELIMITER $$
CREATE TRIGGER table_a_auto_increment
BEFORE INSERT ON table_a
FOR EACH ROW BEGIN
SET NEW.id = (
SELECT IFNULL(MAX(id), 0) + 1
FROM table_a
WHERE table_a.fk_table_b = NEW.fk_table_b
);
END $$
DELIMITER ;
But when I do SELECT LAST_INSERT_ID() I am getting 0 as the new id ... Normally you could override the LAST_INSERT_ID() by giving it a number like this :
INSERT table_a ( fk_table_b, id)
VALUES (1, LAST_INSERT_ID(5));
SELECT LAST_INSERT_ID(); -- -> it gives me 5
So I have tried to combine both to do this trigger :
DELIMITER $$
CREATE TRIGGER table_a_auto_increment
BEFORE INSERT ON table_a
FOR EACH ROW BEGIN
SET NEW.id = (
SELECT LAST_INSERT_ID(IFNULL(MAX(id), 0) + 1)
FROM table_a
WHERE table_a.fk_table_b = NEW.fk_table_b
);
END $$
DELIMITER ;
But it's still giving me 0 when I insert something in the base ... Do you know if there is a way to make it work ?
Thanks a lot.
-- EDIT 2020-08-14
Finally it seems impossible to override the LAST_INSERT_ID function inside the TRIGGER, so I changed my solution by removing the trigger and doing it inside my insert function like this :
INSERT table_a ( fk_table_b, id, label)
VALUES (1, LAST_INSERT_ID((
SELECT IFNULL(MAX(old_one.id), 0) + 1
FROM table_a AS old_one
WHERE old_one.fk_table_b = table_a.fk_table_b
)), "something");
And then, this is giving me the good result I can use in my backend :)
You may use additional service table:
CREATE TABLE service_table (id BIGINT AUTO_INCREMENT PRIMARY KEY);
and
DELIMITER $$
CREATE TRIGGER table_a_auto_increment
BEFORE INSERT ON table_a
FOR EACH ROW
BEGIN
SET NEW.id = (
SELECT IFNULL(MAX(id), 0) + 1
FROM table_a
WHERE table_a.fk_table_b = NEW.fk_table_b
);
DELETE FROM service_table WHERE id IS NOT NULL;
INSERT INTO service_table VALUES (NEW.id - 1);
INSERT INTO service_table VALUES (NULL);
SET NEW.id = LAST_INSERT_ID() - 1;
END $$
DELIMITER ;
fiddle (foreign key removed).
Maybe the code may be simplified a little - do it yourself.
Service table may be defined as Engine = MEMORY (if available).
The code is not safe for concurrent inserts.
How would I go about inserting while incrementing and using LAST_INSERT_ID? I have a table and trigger as such:
CREATE TABLE A(
id int NOT NULL AUTO_INCREMENT,
name char(15),
PRIMARY KEY(id)
);
CREATE TABLE B(
id int NOT NULL,
name char(15),
FOREIGN KEY(id) REFERENCES A(id)
);
delimiter //
CREATE TRIGGER T
AFTER INSERT ON B
FOR EACH ROW
BEGIN
IF (NEW.name LIKE 'A') THEN
INSERT INTO A VALUES(LAST_INSERT_ID() + 1, 'A');
END IF;
END//
delimiter ;
I know that INSERT INTO B VALUES(LAST_INSERT_ID() + 1, 'A'); doesn't work if I have multiple inserts into B because LAST_INSERT_ID() when doing multiple-row inserts, LAST_INSERT_ID() will return the value of the first row inserted (not the last). How would I go about incrementing the ID so that LAST_INSERT_ID() returns the most recent ID from the inserts`
If you want to insert the id from B, then use the id column:
CREATE TRIGGER T
AFTER INSERT ON B
FOR EACH ROW
BEGIN
IF (NEW.name LIKE 'A') THEN
INSERT INTO A VALUES(B.ID + 1, 'A');
END IF;
END//
This seems like a curious expression. Perhaps your real intention is simply to have an auto incremented id on both tables.
I realized that I could insert into A without id since its auto-incremented.
I have a table:
table user(
id_user,
userName,
email,
primary key(id_user)
);
I added unique index on it:
alter table user add unique index(userName, email);
Now I have two indexs on the table:
Index:
Keyname Unique Field
PRIMARY Yes id_user
userName Yes userName, email
The task is to find the MySQL statement for fastest way to insert new unique record.
Statement should return Id_user of the new or existent record.
I'm considering these 2 options, and don't know which is better or is there some third better way to do this?:
1.
INSERT INTO `user` (`userName`, `email`)
VALUES (u1,'u1#email.me' )
ON DUPLICATE KEY Ignore
Q: Where in this statement should be specified that the required KEY for unique inserts is Keyname = uesrName?
2.
IF EXISTS(SELECT `userName`, `email` FROM user WHERE `userName` = u1 AND `email` = u1#email.me)
BEGIN
END
ELSE
BEGIN
INSERT INTO user(`userName`, `email`)
VALUES (u1, u1#email.me);
END IF;
Q: In this statement - how the index with Keyname = userName should be taken in consideration?
Thanks!
The only way to get data out of a table in MySQL is to select.
DELIMITER $$
CREATE FUNCTION ForceUser(pUsername varchar(255), pEmail varchar(255))
RETURNS integer
BEGIN
DECLARE MyId INTEGER;
/*First do a select to see if record exists:*/
/*because (username,email) is a unique key this will return null or a unique id.*/
SELECT id INTO MyId FROM user
WHERE username = pUsername
AND email = pEmail;
IF MyId IS NULL THEN
/*If not then insert the record*/
INSERT INTO user (username, email) VALUES (pUserName,pEmail);
SELECT LAST_INSERT_ID() INTO MyId;
END IF;
RETURN MyID;
END $$
DELIMITER ;
Q: Where in this statement should be specified that the required KEY for unique inserts is Keyname = uesrName?
A: MySQL already knows this, because that information is part of the table definition.
See: http://dev.mysql.com/doc/refman/5.0/en/getting-unique-id.html