SIGNAL SQLSTATE '45000' Doesn't stop Insert - mysql

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

Related

How can I cancel insertion inside the trigger?

Inside the History table, The COL1 shouldn't be like COL2, If was equal then cancel the insertion.
How can I do it?
For MySQL 5.7 use
CREATE TRIGGER prevent_self_referencing
BEFORE INSERT
ON tablename
FOR EACH ROW
BEGIN
IF NEW.column1 = NEW.column2 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Self-referencing not allowed.';
END IF;
END
And the same trigger for BEFORE UPDATE.
In shown window insert only 3 code lines, the whole IF statement.

How to commit transaction before raise in mysql trigger?

CREATE TRIGGER maindb.before_acc_update
BEFORE UPDATE
ON maindb.acc FOR EACH ROW
BEGIN
DECLARE errorMessage VARCHAR(255);
DECLARE clientName VARCHAR(100);
SET clientName = (select USER());
SET errorMessage = CONCAT('DB Error=> new acc_name value (',
NEW.acc_name,
') cannot be null or empty. Old Value is:',
OLD.acc_name);
IF new.acc_name is null or TRIM(COALESCE(new.acc_name , '')) = '' THEN
BEGIN
INSERT INTO maindb.acc_log_error (old_tz, new_tz, username) values (old.acc_name , new.acc_name, clientName);
END ;
SIGNAL SQLSTATE '23000' SET MESSAGE_TEXT = errorMessage;
END IF;
END;
Mysql cancels my transaction. I'd like to log to maindb.acc_log_error and raise my custom error.
Sorry, if you raise a signal in a trigger, it aborts the action of that trigger, including all changes made by that trigge, e.g. your INSERT into the log table.
The only way you could keep the insert to the log table after the trigger aborts is to do that INSERT as a separate statement before or after your UPDATE to acc that spawned the trigger.

unique constraint when column value is equal to false?

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;

Can MySQL TRIGGER check all INSERT values separetly?

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.

I want that the studentid table field with zreo value is not insert,i do this by using trigger but it still allow to insert them

Still allow insert zero value..
CREATE TRIGGER check_my_constraint BEFORE insert ON `personal_details`
FOR EACH ROW
BEGIN
DECLARE msg varchar(255);
IF (NEW.studentid<0)
THEN
SET msg = concat('Constraint my_constraint violated: studentid must not be zero ', cast(new.studentid as char));
SIGNAL sqlstate '45000' SET message_text = msg;
END IF;
END ;
You only check for less than zero. You will have to check for less than or equal to zero:
IF (NEW.studentid <= 0)
Change the condition -
IF (NEW.studentid<0) => IF (NEW.studentid <= 0)