I have two tables:
CREATE TABLE userTypes (
id INTEGER NOT NULL PRIMARY KEY,
type VARCHAR(50) NOT NULL
);
CREATE TABLE users (
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
email VARCHAR(50) NOT NULL,
userTypeId INTEGER NOT NULL,
FOREIGN KEY (userTypeId) REFERENCES userTypes(id)
);
INSERT INTO userTypes (id, type) VALUES (0, 'free');
INSERT INTO userTypes (id, type) VALUES (1, 'paid');
I want to create a procedure where that it inserts a user in the users table, with :
id is auto incremented.
email is equal to the email parameter.
userTypeId is the id of the userTypes row whose type attribute is equal to the type parameter
The INSERT function doesn't work with WHERE, so I tried to add a UPDATE but it's not working. Here's what I have for the moment:
DELIMITER //
CREATE PROCEDURE insertUser(
IN type VARCHAR(50),
IN email VARCHAR(50)
)
BEGIN
INSERT INTO users(id, email, userTypeID) VALUES (LAST_INSERT_ID(), email, userTypeID);
UPDATE users SET users.userTypeID = usertypes.id
WHERE usertypes.type = type;
END//
DELIMITER ;
The expected result should be something like this:
CALL insertUser('free', 'name_1#mail.com');
CALL insertUser('paid', 'name_2#mail.com');
SELECT * FROM users;
id email userTypeId
------------------------------------------
1 name_1#mail.com 0
2 name_2#mail.com 1
Leave out the auto_increment-column. As the name suggests, the db will fill this automatically.
Then, use different names for the parameters than the column names. You can use a prefix with the parameters.
Additionally, you could consider using the userTypeId integer value as parameter instead of the textual value. Otherwise you need to handle the situation where the passed type is not among the types in the userTypes (create a new one/reject insert).
DELIMITER //
CREATE PROCEDURE insertUser(
in_type VARCHAR(50),
in_email VARCHAR(50)
)
BEGIN
INSERT INTO users(email, userTypeID)
SELECT in_email, id
FROM userTypes
WHERE type=in_type;
IF (ROW_COUNT()=0) THEN
SELECT 'Error';
ELSE
SELECT 'OK';
END IF;
END//
DELIMITER ;
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;
DROP TABLE IF EXISTS Sales;
CREATE TABLE Sales (
id INT AUTO_INCREMENT,
product VARCHAR(100) NOT NULL,
quantity INT NOT NULL DEFAULT 0,
fiscalYear SMALLINT NOT NULL,
fiscalMonth TINYINT NOT NULL,
CHECK(fiscalMonth >= 1 AND fiscalMonth <= 12),
CHECK(fiscalYear BETWEEN 2000 and 2050),
CHECK (quantity >=0),
UNIQUE(product, fiscalYear, fiscalMonth),
PRIMARY KEY(id)
);
DROP TABLE IF EXISTS log;
CREATE TABLE log (
id INT AUTO_INCREMENT PRIMARY KEY,
timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
text VARCHAR(100)
);
Triggers
CREATE DEFINER=`root`#`localhost` TRIGGER `sales_AFTER_UPDATE` AFTER UPDATE ON `sales`
FOR EACH ROW
BEGIN
INSERT INTO log VALUES(NOW(),CONCAT('Update Student Record ', OLD.quantity));
END
UPDATE test for.sales SET quantity = 36
WHERE (id = 1);
ERROR 1136: 1136: Column count doesn't match value count at row 1
Iam new in mySQL Please help
You should specify columns in INSERT statement in your trigger explicitly, as you do not set all values in a row (auto incremented column excluded).
So it would be
INSERT INTO log(timestamp, text) VALUES (NOW(),CONCAT('Update Student Record ', OLD.quantity));
You have some errors.
First based on your trigger you need another column on log table which is as following
quantity INT NOT NULL DEFAULT 0
Second , do not use Keywords and Reserved Words like text and timestamp, it is a bad practice. If you do please put it inside backticks
Third your insert statement should be
INSERT INTO log(`timestamp`,`text`,quantity) VALUES(NOW(),'Update Student Record', OLD.quantity);
there is no need for CONCAT.
Fourth,
`sales`
table is not the same as Sales table, because you have used backticks.
Full working trigger below:
DELIMITER //
CREATE TRIGGER `sales_AFTER_UPDATE` AFTER UPDATE ON Sales
FOR EACH ROW
BEGIN
INSERT INTO log(`timestamp`,`text`,quantity) VALUES(NOW(),'Update Student Record', OLD.quantity);
END//
DELIMITER ;
Check working demo:
https://www.db-fiddle.com/f/iqwShcHK3AGJvU4MDbxDku/0
I am creating a stored procedure to register a user.
Here is the create:
CREATE TABLE User (
UID INT AUTO_INCREMENT,
Fname VARCHAR(32) NOT NULL,
Lname VARCHAR(32) NOT NULL,
Phone VARCHAR(32),
Email VARCHAR(50),
Pass VARCHAR(32),
CONSTRAINT User_PK PRIMARY KEY(EmpID),
CONSTRAINT User_UK1 UNIQUE (FName, LName, Phone),
CONSTRAINT User_UK2 UNIQUE (FName, LName, Email),
CONSTRAINT User_UK3 UNIQUE (Email, Pass)
)ENGINE=InnoDB;
Here is the stored procedure so far:
DELIMITER //
CREATE PROCEDURE regUser(fname VARCHAR(32),lname VARCHAR(32),phone VARCHAR(32), email VARCHAR(50), pass VARCHAR(32))
BEGIN
DECLARE uid INT;
DECLARE CONTINUE HANDLER FOR 1062 BEGIN END;
START TRANSACTION;
INSERT INTO User(Fname, Lname, Phone, Email, Pass) VALUES(fname, lname, phone, email, pass);
SELECT UID FROM User WHERE Phone=phone OR Email=email AND Fname=fname AND Lname=lname;
COMMIT;
END //
DELIMITER ;
In the stored procedure it attempts the insert, if a duplicate entry is found the continue handler(1062) catches the error and doesn't do anything. After the insert (or lack of) I want to select the UID of the inserted row, or of the pre-existing row (if no insert was done due to duplicate)
What is happening: The SELECT UID..WHERE... is returning a column of 4 NULL values instead of the UID of the row WHERE Phone=phone OR Email=email AND Fname=fname AND Lname=lname;
I believe the 4 NULL values are because I have 4 Users in the User table at the moment.
I am completely puzzled on this, if I do the SELECT UID..WHERE... outside of the stored procedure, it works just fine, returning the UID WHERE Phone=phone...
I get the same result NULL when inserting a User that is already in the table and when trying a new unique user.
I have multiple user_roles. Each user_role has multiple privileges and each privileges has multiple values. I need to create a procedure with user_role_name,description,priviliges_fk(array),values(arrayofstring) as inputs.
This is the procedure I have written.
DELIMITER $$
DROP PROCEDURE IF EXISTS `save_role`$$
CREATE DEFINER=`event_admin`#`%` PROCEDURE `save_role`(IN p_role_name INT,
IN p_description INT,
IN p_privilege_fk INT(),
IN p_values VARCHAR(1000)
)
BEGIN
DECLARE i int default 0;
DECLARE V_ROLE_FK int;
DECLARE counter INT DEFAULT 0;
INSERT INTO ROLE (ROLE_NAME,DESCRIPTION) VALUES(p_role_name,p_description);
SELECT ROLE_PK INTO V_ROLE_FK FROM ROLE WHERE ROLE_NAME=p_role_name AND DESCRIPTION=p_description;
simple_loop:LOOP
SET counter = counter + 1;
INSERT INTO ROLE_PRIVILEGE_BRIDGE (ROLE_FK,PRIVILEGE_FK,VALUE) VALUES(V_ROLE_FK,p_privilege_fk(i),p_values);
END LOOP simple_loop;
END;
You can't. There are two workarounds that would work
Call the procedure one time per element in the array
Concatenate the array elements into one string separated by something (ie |, ;, :) and then split that string internally in the procedure.
I would go with the first alternative. It's cleaner, easier to understand and easier to test.
I'd suggest you to use AUTO_INCREMENT option for primary keys, it will help to work with them. Then use auto-incremented primary key values to insert new rows into a child table - one by one, not using array as a string parameter.
For example (data is simplified):
CREATE TABLE ROLE(
ID INT(11) NOT NULL AUTO_INCREMENT,
ROLE_NAME INT,
DESCRIPTION INT,
PRIMARY KEY (ID)
)
ENGINE = INNODB;
CREATE TABLE ROLE_PRIVILEGE_BRIDGE(
ID INT(11) NOT NULL AUTO_INCREMENT,
PRIVILEGE_FK INT(11) DEFAULT NULL,
VALUE INT(11) DEFAULT NULL,
PRIMARY KEY (ID),
CONSTRAINT FK FOREIGN KEY (PRIVILEGE_FK) REFERENCES ROLE (ID)
)
ENGINE = INNODB;
INSERT INTO ROLE(ROLE_NAME, DESCRIPTION) VALUES(1, 1);
SET #new_id = LAST_INSERT_ID();
INSERT INTO ROLE_PRIVILEGE_BRIDGE(PRIVILEGE_FK, VALUE) VALUES (#new_id, 1);
INSERT INTO ROLE_PRIVILEGE_BRIDGE(PRIVILEGE_FK, VALUE) VALUES (#new_id, 2);
INSERT INTO ROLE_PRIVILEGE_BRIDGE(PRIVILEGE_FK, VALUE) VALUES (#new_id, 3);
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