I've got simple database with tables: Sklep(SklepNazwa,Adres), Towar(TowarNazwa,Producent), Sprzedaz(SklepNazwa,SklepAdres,TowarNazwa,TowarProducent,Cena,Data)
I had to create TRIGGER like shown below.
Problem is that it don't work when I try to INSERT several rows at once. When at least one row triggers SIGNAL all values are dumped. Nothing gets inserted, even when it is ok to be inserted.
Can I somehow call
INSERT INTO Sprzedaz
VALUES (...),(...),(...),(...);
where some values are ok to be inserted and some should fail and show SIGNAL message?
DELIMITER $$
CREATE TRIGGER SprzedazInsert
BEFORE INSERT ON `Sprzedaz`
FOR EACH ROW
BEGIN
IF (SELECT COUNT(1) FROM Sprzedaz
WHERE NEW.SklepNazwa = Sprzedaz.SklepNazwa
AND NEW.SklepAdres = Sprzedaz.SklepAdres
AND NEW.TowarNazwa = Sprzedaz.TowarNazwa
AND NEW.TowarProducent = Sprzedaz.TowarProducent
AND NEW.Cena = Sprzedaz.Cena
AND NEW.`Data` = Sprzedaz.`Data`) > 0
THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'INSERT FAILED: Row already exists.';
ELSE
IF (SELECT COUNT(1) FROM Sklep WHERE NEW.SklepNazwa = Sklep.SklepNazwa) > 0
THEN
IF (NEW.SklepAdres != (SELECT Adres FROM Sklep WHERE NEW.SklepNazwa = Sklep.SklepNazwa))
THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'INSERT FAILED: Wrong "Sklep" data.';
END IF;
ELSE
INSERT INTO `Sklep` (`SklepNazwa`,`Adres`)
VALUES (NEW.SklepNazwa,NEW.SklepAdres);
END IF;
IF (SELECT COUNT(1) FROM Towar WHERE NEW.TowarNazwa = Towar.TowarNazwa) > 0
THEN
IF (NEW.TowarProducent != (SELECT Producent FROM Towar WHERE NEW.TowarNazwa = Towar.TowarNazwa))
THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'INSERT FAILED: Wrong "Towar" data.';
END IF;
ELSE
INSERT INTO `Towar` (`TowarNazwa`,`Producent`)
VALUES (NEW.TowarNazwa,NEW.TowarProducent);
END IF;
END IF;
END$$
If you try to insert several rows in the same transaction, the dbms assumes you want all the inserts to succeed or fail as a group. If you want rows to succeed or fail individually, you need to commit after each insert.
Related
I am having problems implementing a trigger into my table. I am using MySQL, Phpmyadmin
Scenario: Table User(user_id, name, surname, date_of_joining), record(record_id, date_of_record, weight, height, id_user)
a user can have multiple records which show his weight and height at a certain date. id_user is a foreign key referencing to user_id.
I am trying to implement a trigger for insert and update which checks if date_of_record is greater than date_of_joining, if not, the insert should be stopped.
This is a trigger I tried and the insert still goes through
CREATE TRIGGER date_check_insert BEFORE INSERT ON record
FOR EACH ROW
BEGIN
DECLARE date_of_registering DATE;
SET date_of_registering = (SELECT date_of_registering FROM user WHERE user_id = new.id_user);
IF (new.date_of_record NOT BETWEEN date_of_registering AND CURDATE()) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "Can not insert that value";
END IF;
END
Any help is appreciated
I'd slightly change the trigger:
Use session variable (so no DECLARE needed)
Put comparison logic in a single place in the WHERE clause (easier to read & understand)
Shows reason in error message
Use SELECT INTO variable instead of SET variable = query (not necessary, I just prefer this way)
So:
CREATE TRIGGER date_check_insert BEFORE INSERT ON record
FOR EACH ROW
BEGIN
SET #found = NULL;
SELECT
user_id INTO #found
FROM
user
WHERE
user_id = NEW.id_user
AND NEW.date_of_record BETWEEN date_of_registering AND NOW() -- or CURDATE() depend on your datetype
;
IF #found IS NULL THEN
SET #msg = CONCAT('Can not insert. Out of range value ', NEW.date_of_record);
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = #msg;
END IF;
END
I seem to be unable to have multiple, non-related if statements in SQL
I have a table of users with a bunch of columns that should be unique, but some are also optional. Since SQL throws an error when more than one fields have a NULL value, I decided to approach the problem with a trigger.
Here is how I wrote it:
create definer = root#localhost trigger `check-if-unique-update`
before UPDATE
on contact
for each row
if new.phone2 is null
then
if (select count(id) from contact where phone2 = new.phone2 or phone = new.phone2) > 0
then
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'There is something wrong with the Contact data you entered!';
end if;
end if;
if new.email2 is not null
then
if (select count(id) from contact where email2 = new.email2 or email = new.email2)
then
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'There is something wrong with the Contact data you entered!';
end if;
end if;
if new.fax is not null
then
end if;
end if;
First off, DataGrip (DataBase management IDE by JetBrains) gives me an error. When I then upload the file to the MySql DataBase it only shows the first if Statement
if new.phone2 is null
then
if (select count(id) from contact where phone2 = new.phone2 or phone = new.phone2) > 0
then
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'There is something wrong with the Contact data you entered!';
end if;
end if;
I feel like I am missing some rudimentary detail, or is this just not possible in sql?
Have you tried including a begin-end in your trigger?
create definer = root#localhost trigger `check-if-unique-update`
before UPDATE
on contact
for each row
begin
if new.phone2 is null
then
if (select count(id) from contact where phone2 = new.phone2 or phone = new.phone2) > 0
then
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'There is something wrong with the Contact data you entered!';
end if;
end if;
if new.email2 is not null
then
if (select count(id) from contact where email2 = new.email2 or email = new.email2)
then
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'There is something wrong with the Contact data you entered!';
end if;
end if;
if new.fax is not null
then
end if;
end if;
end
Depending on your IDE, you may need to change the delimiter to // for example.
For reference see: https://dev.mysql.com/doc/refman/8.0/en/trigger-syntax.html
In mysql is it possible to have a unique constraint only when a value is false?
For example, I want a unique constraint on Name but only if Archived = false.
In other words I have a field archived which I'm using as a soft delete and deleting two things with the same name will result in a violation if I put the unique constraitn around Name and archived. hence wondering if the database can handle this type of unique constraint
Relevant Example
I think the only way to do this would be with Triggers, and Signals.
Just put your trigger for update and insert.
Relevant Example of Similar Approach From here: http://cvuorinen.net/2013/05/validating-data-with-triggers-in-mysql/
DELIMITER $$
CREATE TRIGGER example_before_insert_allow_only_one_active
BEFORE INSERT ON example_tbl FOR EACH ROW
BEGIN
IF NEW.active = 1 AND (SELECT COUNT(id) FROM example_tbl
WHERE active=1 AND foreign_key_id=NEW.foreign_key_id) > 0
THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Cannot add or update row: only one active row allowed per type';
END IF;
END;
$$
CREATE TRIGGER example_before_update_allow_only_one_active
BEFORE UPDATE ON example_tbl FOR EACH ROW
BEGIN
IF NEW.active = 1 AND (SELECT COUNT(id) FROM example_tbl
WHERE id<>NEW.id AND active=1 AND foreign_key_id=NEW.foreign_key_id) > 0
THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Cannot add or update row: only one active row allowed per type';
END IF;
END;
$$
Applied to your case
DELIMITER $$
CREATE TRIGGER example_before_insert_allow_only_one_not_archived
BEFORE INSERT ON example_tbl FOR EACH ROW
BEGIN
IF NEW.archived = 0 AND (SELECT COUNT(id) FROM example_tbl
WHERE name=NEW.name) > 0
THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Cannot add or update row: existing name exists and you are entering with archived = 0 (false)';
END IF;
END;
$$
CREATE TRIGGER example_before_update_allow_only_one_not_archived
BEFORE UPDATE ON example_tbl FOR EACH ROW
BEGIN
IF NEW.archived = 0 AND (SELECT COUNT(id) FROM example_tbl
WHERE name=NEW.name) > 0
THEN
SIGNAL SQLSTATE '45000'
S SET MESSAGE_TEXT = 'Cannot add or update row: existing name exists and you are entering with archived = 0 (false)';
END IF;
END;
$$
Finally, you may actually be interested in using the following statement in your SQL if you only want one NAME for archived = false, but unlimited for archived = true.
SELECT COUNT(id) FROM example_tbl WHERE name=NEW.name AND archived = 0;
I have simple project for database classes there i have shop database which implement such logic: for each customer in order table we have order with type = 'CART' when customer confirm his order his order change it type to 'ORDER' and we create a new CART for our customer.
Now i want to write trigger that allows me to control that each customer has only one CART.
I write something like this
DELIMITER $$
USE `newshop`$$
CREATE
DEFINER=`root`#`localhost`
TRIGGER `newshop`.`cart_check`
BEFORE INSERT ON `newshop`.`order`
FOR EACH ROW
BEGIN
DECLARE msg VARCHAR(255);
DECLARE count_pn INTEGER;
SELECT count(*) INTO count_pn FROM newshop.order
where newshop.order.type = NEW.type
and NEW.user_id = newshop.order.user_id
and NEW.type = 'CART'
or NEW.type = 'cart'
LIMIT 1;
if count_pn > 0 THEN
BEGIN
set msg = 'Oh no';
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = msg;
end;
ELSE
BEGIN
set msg = 'Oh yeah';
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = msg;
END;
END IF;
END$$
then when i try to insert into table new order with type cart for user with id = 1, that already has a cart - this trigger doesn't allow me to do this and if i try to add a cart for another user, that doesn't have cart - this trigger also doesn't allow me to do this.
In else, you also have the SIGNAL SQLSTATE=45000, which is unhandled exception, it will cause return as failed.
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = msg;
DELIMITER $$
CREATE TRIGGER insertTrigger BEFORE INSERT ON `agents`
FOR EACH ROW
BEGIN
DECLARE groupID int;
SET groupID = 0;
SET groupID = (SELECT id FROM `groups` WHERE `id` = NEW.group_id);
IF (groupID != 0) THEN
PRINT 'ID is ' + groupID;
ROLLBACK;
END IF;
END$$
DELIMITER ;
The above trigger is created for checking if the foreign key ID exists in the group table.
1) How do I print error messages in mySQL?
2) ROLLBACK Function doesn't work. It gave me the following error message.
"#1422 - Explicit or implicit commit is not allowed in stored function or trigger."
You can use SIGNAL statement to raise an error with custom message from the trigger.
...
IF (groupID != 0) THEN
SET #msg = CONCAT('ID is ', groupID);
SIGNAL SQLSTATE '02000' SET MESSAGE_TEXT = #msg;
END IF;
...
If you want just warning message, then use SQLSTATE code starting with '01...'.