MYSQL TRIGGER with IF condition - mysql

I'm a noob im MYSQL and I'm trying to make a trigger who will auto-fill 2 fields(customername, customersurname) in an invoice table. Can anyoane explain me please what I do wrong in that trigger? Thank you very much! :)
CREATE TABLE customer(
customerID INT(6) PRIMARY KEY AUTO_INCREMENT,
customername VARCHAR(20) NOT NULL,
customersurname VARCHAR(20) NOT NULL,
customeraddress VARCHAR(200)
);
CREATE TABLE invoice(
invoiceID INT (6) Primary KEY AUTO_INCREMENT,
customerID INT (6) REFERENCES customer(customerID),
customername VARCHAR(20) REFERENCES customer(customername),
invoicedate DATE,
orderID INT(6) REFERENCES orders(orderID),
products VARCHAR(200) REFERENCES orderlines(productname),
FOREIGN KEY(orderID) REFERENCES orders(orderID)
);
CREATE TRIGGER autofill_invoice BEFORE INSERT ON invoice FOR EACH ROW
BEGIN
IF (new.customerID = customer.customerID,
new.customersurname = customer.customersurname )
THEN
SET new.customername = customer.customername,
new.customersurname = customer.customersurname;
END IF;
END;

As mysql documentation on defining stored programs explains:
If you use the mysql client program to define a stored program containing semicolon characters, a problem arises. By default, mysql itself recognizes the semicolon as a statement delimiter, so you must redefine the delimiter temporarily to cause mysql to pass the entire stored program definition to the server.
To redefine the mysql delimiter, use the delimiter command. The
following example shows how to do this for the dorepeat() procedure
just shown. The delimiter is changed to // to enable the entire
definition to be passed to the server as a single statement, and then
restored to ; before invoking the procedure. This enables the ;
delimiter used in the procedure body to be passed through to the
server rather than being interpreted by mysql itself.
So, use the delimiter command when you define the stored procedure:
DELIMITER //
CREATE TRIGGER autofill_invoice BEFORE INSERT ON invoice FOR EACH ROW
BEGIN
IF (new.customerID = customer.customerID,
new.customersurname = customer.customersurname )
THEN
SET new.customername = customer.customername,
new.customersurname = customer.customersurname;
END IF;
END//
DELIMITER ;
Next time pls provide exact error description in your question!

Related

Constrain grandchild table by grandparent key through parent [MySQL] [duplicate]

I would like to add a constraint that will check values from related table.
I have 3 tables:
CREATE TABLE somethink_usr_rel (
user_id BIGINT NOT NULL,
stomethink_id BIGINT NOT NULL
);
CREATE TABLE usr (
id BIGINT NOT NULL,
role_id BIGINT NOT NULL
);
CREATE TABLE role (
id BIGINT NOT NULL,
type BIGINT NOT NULL
);
(If you want me to put constraint with FK let me know.)
I want to add a constraint to somethink_usr_rel that checks type in role ("two tables away"), e.g.:
ALTER TABLE somethink_usr_rel
ADD CONSTRAINT CH_sm_usr_type_check
CHECK (usr.role.type = 'SOME_ENUM');
I tried to do this with JOINs but didn't succeed. Any idea how to achieve it?
CHECK constraints cannot currently reference other tables. The manual:
Currently, CHECK expressions cannot contain subqueries nor refer to
variables other than columns of the current row.
One way is to use a trigger like demonstrated by #Wolph.
A clean solution without triggers: add redundant columns and include them in FOREIGN KEY constraints, which are the first choice to enforce referential integrity. Related answer on dba.SE with detailed instructions:
Enforcing constraints “two tables away”
Another option would be to "fake" an IMMUTABLE function doing the check and use that in a CHECK constraint. Postgres will allow this, but be aware of possible caveats. Best make that a NOT VALID constraint. See:
Disable all constraints and table checks while restoring a dump
A CHECK constraint is not an option if you need joins. You can create a trigger which raises an error instead.
Have a look at this example: http://www.postgresql.org/docs/9.1/static/plpgsql-trigger.html#PLPGSQL-TRIGGER-EXAMPLE
CREATE TABLE emp (
empname text,
salary integer,
last_date timestamp,
last_user text
);
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
BEGIN
-- Check that empname and salary are given
IF NEW.empname IS NULL THEN
RAISE EXCEPTION 'empname cannot be null';
END IF;
IF NEW.salary IS NULL THEN
RAISE EXCEPTION '% cannot have null salary', NEW.empname;
END IF;
-- Who works for us when she must pay for it?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% cannot have a negative salary', NEW.empname;
END IF;
-- Remember who changed the payroll when
NEW.last_date := current_timestamp;
NEW.last_user := current_user;
RETURN NEW;
END;
$emp_stamp$ LANGUAGE plpgsql;
CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp
FOR EACH ROW EXECUTE PROCEDURE emp_stamp();
...i did it so (nazwa=user name, firma = company name) :
CREATE TABLE users
(
id bigserial CONSTRAINT firstkey PRIMARY KEY,
nazwa character varying(20),
firma character varying(50)
);
CREATE TABLE test
(
id bigserial CONSTRAINT firstkey PRIMARY KEY,
firma character varying(50),
towar character varying(20),
nazwisko character varying(20)
);
ALTER TABLE public.test ENABLE ROW LEVEL SECURITY;
CREATE OR REPLACE FUNCTION whoIAM3() RETURNS varchar(50) as $$
declare
result varchar(50);
BEGIN
select into result users.firma from users where users.nazwa = current_user;
return result;
END;
$$ LANGUAGE plpgsql;
CREATE POLICY user_policy ON public.test
USING (firma = whoIAM3());
CREATE FUNCTION test_trigger_function()
RETURNS trigger AS $$
BEGIN
NEW.firma:=whoIam3();
return NEW;
END
$$ LANGUAGE 'plpgsql'
CREATE TRIGGER test_trigger_insert BEFORE INSERT ON test FOR EACH ROW EXECUTE PROCEDURE test_trigger_function();

Performing SELECT but INSERT when NOT EXISTS in MySQL

I have a MySQL table created using the following syntax:
CREATE TABLE `name_to_id` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(128),
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
);
And a common query this table would like to answer is name to id look-up, but if the <name, id> pair does not exist in the DB, then also insert a new entry and return the newly inserted id.
Can I know should I do that in MySQL?
As commented by Strawberry, this cannot be performed in a single query.
However, here is a stored procedure that should do what you expect. First, it uses the INSERT ... ON DUPLICATE KEYS UPDATE syntax to insert new names ; this actually relies on the UNIQUE key that you correctly set up on the name column.
DELIMITER //
CREATE PROCEDURE get_id_by_name(IN p_name VARCHAR(128))
BEGIN
INSERT INTO name_to_id(name) VALUE(p_name) ON DUPLICATE KEY UPDATE name = p_name;
SELECT id FROM name_to_id WHERE name = p_name;
END //
DELIMITER ;
Demo on DB Fiddle.
This approach is efficient, but the downside of ON DUPLICATE KEYS is that it wastes id sequences : everytime the query is called, the sequence is autoincremented (even if a record already exists). This can be seen in the fiddle.
Here is another approach, that won't burn sequence numbers :
DELIMITER //
CREATE PROCEDURE get_id_by_name(IN p_name VARCHAR(128))
BEGIN
DECLARE p_id bigint(20) unsigned;
SELECT id INTO p_id FROM name_to_id WHERE name = p_name;
IF (p_id IS NULL) THEN
INSERT INTO name_to_id(name) VALUE(p_name);
SELECT LAST_INSERT_ID();
ELSE
SELECT p_id;
END IF;
END //
DELIMITER ;
Demo on DB Fiddle.
you can do this on stored proc, if the select statement did not return a result, then you can execute the insert statement

Error while creating a procedure mysql CRUD

I keep getting a syntax error at line 9
CREATE PROCEDURE `ItemsAddOrEdit`(
_itm_id INT,
_itm_name VARCHAR(255),
_itm_price FLOAT(8,2)
)
BEGIN
IF _itm_id = 0 THEN
INSERT INTO items (itm_name, itm_price)
VALUES (_itm_name, _itm_price);
ELSE
UPDATE items
SET
itm_name = _itm_name,
itm_price = _itm_price
WHERE itm_id = _itm_id;
END IF;
END
Are the variables the problem? I've checked the table to see if I messed the names up, but it all seems fine to me.
Here's the table code
CREATE TABLE `items` (
`itm_id` INT(255) NOT NULL AUTO_INCREMENT,
`itm_name` VARCHAR(255) NOT NULL,
`itm_price` FLOAT(8,2) NOT NULL,
PRIMARY KEY (`itm_id`),
UNIQUE INDEX `itm_name` (`itm_name`)
)
You need to redefine Delimiter to something else, for eg: $$. This allows parser to ignore ; (hence do not execute statement on reaching ;).
Also, as a good practice, always use DROP PROCEDURE IF EXISTS, to avoid failing out in case procedure with same name already exists.
At the end, redefine the Delimiter back to ;
Try the following:
DELIMITER $$
DROP PROCEDURE IF EXISTS `ItemsAddOrEdit` $$
CREATE PROCEDURE `ItemsAddOrEdit`(
_itm_id INT,
_itm_name VARCHAR(255),
_itm_price FLOAT(8,2)
)
BEGIN
IF _itm_id = 0 THEN
INSERT INTO items (itm_name, itm_price)
VALUES (_itm_name, _itm_price);
ELSE
UPDATE items
SET
itm_name = _itm_name,
itm_price = _itm_price
WHERE itm_id = _itm_id;
END IF;
END $$
DELIMITER ;

How to Create Stored Procedure in MySQL

My tables are
create table employee(
id int(10) auto_increment primary key,
name varchar(100),
addressId int(10)
);
go
create table address(
id varchar(10) auto_increment primary key,
name varchar(100)
);
Here is my procedure
create procedure insert_employee(IN emp_name varchar(100),IN emp_address varchar(100))
begin
DECLARE #addressId varchar(10);
SELECT #addressId:=id from address where name LIKE '%'+emp_address+'%';
IF #addressId = ''
THEN
set #addressId= 'DBS-2136';-- It will come form function
INSERT INTO address values(#addressId,emp_address);
END IF
INSERT INTO employee values(emp_name,#addressId);
END
I don't understand what is the problem. If i write this type of if condition in ms sql server there is not error. every time execute the procedure ti say error in end if. I have search in google but there is not idea about this. there is a problem in declare variable. If i copy form mysql documentation that also not work. why is that?
please help me
1. What is the proper way to declare variable under mysql stored procedure,
2. how to write if condition in mysql stored procedure.
thank you
Lots of differences between mysql and mssql. Declared variables should not include '#', all statements must be terminated, + is an arithmetic operator, if you procedure has multiple statements you must set delimiters before and after.
Further reading
How to declare a variable in MySQL?
https://dev.mysql.com/doc/refman/8.0/en/stored-programs-defining.html
From MySQL reference Manual
An IF ... END IF block, like all other flow-control blocks used within stored programs, must be terminated with a semicolon
IF #addressId = ''
THEN
set #addressId= 'DBS-2136';-- It will come form function
INSERT INTO address values(#addressId,emp_address);
END IF;

MySQL Stored procedure for updating two tables (2nd with auto increment value from 1st)

I want to update two tables at the same time in my database. One table is for groups, and the other table is for members of groups:
CREATE TABLE IF NOT EXISTS groups (
group_id INTEGER UNSIGNED AUTO_INCREMENT,
group_name VARCHAR(150) NOT NULL DEFAULT '',
group_created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (group_id)
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8;
CREATE TABLE IF NOT EXISTS group_members (
group_mem_user_id INTEGER UNSIGNED NOT NULL,
group_mem_group_id INTEGER UNSIGNED NOT NULL,
group_mem_role TINYINT DEFAULT 1,
group_mem_created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT group_mem_pk PRIMARY KEY (group_mem_user_id, group_mem_group_id),
FOREIGN KEY (group_mem_user_id) REFERENCES user (user_id),
FOREIGN KEY (group_mem_group_id) REFERENCES groups (group_id)
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8;
I want to use a stored procedure to create an entry in group and create an entry in group_members with the id that was just created for the group.
I know how to do this on the server (I have a java server and I'm using Spring's JdbcTemplate to make calls to the database) but I thought it would be better and more efficient to do this in a stored procedure.
The two individual queries are (im using prepared statements):
INSERT INTO groups (group_name) VALUES (?)
and
INSERT INTO group_members (group_mem_user_id, group_mem_group_id, group_mem_role) VALUES (?,?,?)
But I'm not sure how to merge these into one stored procedure.
DELIMITER //
DROP PROCEDURE IF EXISTS create_group //
CREATE PROCEDURE create_group(
#in/out here
)
BEGIN
#no idea
END //
DELIMITER ;
Ideally I would like it to return some value describing whether the operation was sucessful or not.
I use the following procedure:
DELIMITER //
DROP PROCEDURE IF EXISTS create_group //
CREATE PROCEDURE create_group(
IN create_group_group_name VARCHAR(150),
IN create_group_user_id INT
)
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION, SQLWARNING
BEGIN
ROLLBACK;
END;
START TRANSACTION;
INSERT INTO groups (group_name) VALUES (create_group_group_name);
INSERT INTO group_members (group_mem_user_id, group_mem_group_id, group_mem_role) VALUES (create_group_user_id, LAST_INSERT_ID(), 2);
COMMIT;
END //
DELIMITER ;
In my server I use it like:
JdbcTemplate jt = new JdbcTemplate(DB.getDataSource(DB_USER));
int i = jt.update("CALL create_group (?,?)", new Object[] {groupName, userId});
if (i != 1)
throw new SQLException("Error creating group with name=" + groupName + " for userid=" + userId);
i == 1 if everything went well. Groups will never be created if the user is not added as a member (fixing the problem with my first iteration below).
OLD
(non-transactional, causes a problem if the second insert fails then the group is created with no members, it might work in some cases which is why I leave it here but it doesn't work for me)
The following procedure works. It does not return anything and I am just using the fact that the procedure completes without error to assume that it was all ok.
DELIMITER //
DROP PROCEDURE IF EXISTS create_group //
CREATE PROCEDURE create_group(
IN create_group_group_name VARCHAR(150),
IN create_group_user_id INT
)
BEGIN
INSERT INTO groups (group_name) VALUES (create_group_group_name);
INSERT INTO group_members (group_mem_user_id, group_mem_group_id, group_mem_role) VALUES (create_group_user_id, LAST_INSERT_ID(), 2);
END //
DELIMITER ;