I have table "Months" with 12 rows(obviously) and I want to add a trigger that prevent adding new row if ID number is over 12
CREATE TRIGGER "no_more_months" BEFORE INSERT ON "months"
FOR EACH ROW BEGIN
IF NEW.ID_Month>12 THEN
SIGNAL SQLSTATE '12345'
SET MESSAGE_TEXT = 'check constraint on Months failed';
END IF;
END;
But somehow it doesn't work and still adds new rows
As said in the comment section, just a constraint should do the job.
ALTER TABLE [dbo].[Months]
ADD CONSTRAINT CK_Months_ID_Months CHECK ([ID_Months] <= 12 AND [ID_Months] > 0);
If you absolutely need a custom error message look at this article : MySql Signals
Related
I want to create a trigger that rejects the insert of an underage employee, by underage I mean that his year of birth is 2004 or more.
I wrote the following code, it runs without errors but then it doesn't let me insert any employee because it says :
ERROR: Unknown column 'BIRTHDATE' IN 'field list'
When I drop the trigger everything works fine.
DELIMITER $$
CREATE TRIGGER REJECT_EMP
BEFORE INSERT ON EMPLOYEE
FOR EACH ROW
BEGIN
IF YEAR(BIRTHDATE) > 2003 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'An error occurred';
END IF;
END$$
DELIMITER ;
You should be testing NEW.BIRTHDATE
'Within the trigger body, the OLD and NEW keywords enable you to access columns in the rows affected by a trigger.' https://dev.mysql.com/doc/refman/8.0/en/trigger-syntax.html
If you are running MySQL 8.0, I would recommend a check constraint rather than trigger logic.
alter table employee
add constraint chk_birthdate
check(year(birthdate) > 2003)
;
This enforces the same check that the trigger does, but the syntax is much shorter, and the logic is bundled directly in the definition of the table (so it is somehow easier to maintain). When an attempt is made to insert an offending row, you get the following error message:
Check constraint 'chk_birthdate' is violated.
I have to advice 2 changes in your trigger:
fist as #P.Salmon answered you should to use NEW.BIRTHDATE parameter
second use age instead birth-year in comparison
DELIMITER $$
CREATE TRIGGER REJECT_EMP
BEFORE INSERT ON EMPLOYEE
FOR EACH ROW
BEGIN
IF TIMESTAMPDIFF(YEAR, NEW.BIRTHDATE, NOW()) < 17 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'An error occurred';
END IF;
END$$
DELIMITER ;
In general, the right way to check year() is on a day-by-day basis. So the logic that you want is:
BEGIN
IF BIRTHDATE > CURDATE() - INTERVAL 16 YEAR THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'An error occurred';
END IF;
END$$
This actually checks the date of birth. So someone can become an employee on their birthday.
I am not sure "16" is the right value, but your question doesn't explain it. It makes sense given the logic you have presented and that the year when you asked the question is 2020.
I have created a basic trigger that checks if the the new value inserted into a row is greater or equal than 1 and less or equal than 10 , this is for just test qualifications , the thing is that wherever I just insert a new test qualification for example 0 it adds it up to the table, but my trigger checks that this value should be greater than 0 and is not working
Trigger that checks if a qualification is greater or equal than 1 or less or equal than 10
delimiter //
CREATE TRIGGER checkQlfy BEFORE
INSERT ON qualify
FOR EACH ROW
BEGIN
IF new.qualify_pp >= 1 and new.qualify_pp <= 10 THEN
INSERT INTO qualify(qualify_pp) VALUES (new.qualify_pp);
END IF;
END//
delimiter ;
Now, this trigger is created succefully but wherever I insert a new value lets say with this line
INSERT INTO qualify(qualify_pp) VALUES(0);
It is inserted into the table, but I have said in the trigger that values greater or equal than 1 should be added.
I dont know why is this happening.
You have two options:
Add a CHECK constraint over the qualify_pp field. Only works in versions >= 8.0.16 (here's why).
You cannot change the insert operation triggering the trigger. You can, however, raise an error if a validation fails so the insert is not performed.
E.g. (see how the validations in the IF changed):
delimiter //
CREATE TRIGGER checkQlfy BEFORE
INSERT ON qualify
FOR EACH ROW
BEGIN
IF new.qualify_pp < 1 OR new.qualify_pp > 10 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Not allowed / some message';
END IF;
END//
delimiter ;
The trigger you have written would do the insert based on:
1) insert statement and trigger insert if 1<=value<=10
2) insert statement and trigger not insert if 1>=value>=10
You would have to add a check constraint in it, rather than having a trigger!
I have a mysql table:
`id` - int (primary key)
`datecolumn` - datetime
`name` - varchar
In my application, I want to enforce a rule that the name column can't be updated if the current date and time are past the datecolumn field's value. Currently, I'm querying the database for the value for each row, and then updating if the current date/time is before.
I'd like to know how to enforce this without the extra call to the database before updating. I'm updating many different rows at a time in the application, and think I'm causing performance issues because of all of the extra queries.
You can enforce rules like this using a trigger.
In your case, you would want a before update trigger. Something like this:
delimiter $$
create trigger noupdates before update on t
for each row
begin
if new.datecolumn > now() then
signal sqlstate '45000'
set message_text := 'Too late to update row';
end if;
end;
$$
delimiter ;
I'm using MySQL. I have a record on A table with a soft delete column: active with a value of 0.
This row is linked to 11 tables. All have the same active column.
I need to be sure that the record on A is deleted only if all the references across the 11 tables have active = 0 also.
I know I can write a view with these queries to get if I can "deleted" or not. But this is one example and IMO not very practical solution. Cascade update won't work either because I can't delete the parent row if any of the child is still active.
Thanks!
This should work if you create the active_view as you said you could. Just add the active flags of all the related tables into the foreign_active column, and you should be good to go.
CREATE TRIGGER before_update_student
BEFORE UPDATE ON student FOR EACH ROW
BEGIN
IF NEW.active = 0 AND (SELECT foreign_active FROM active_view
WHERE id = NEW.id) > 0
THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Cannot delete student when active roles exist.';
END IF;
END;
I have a row in a table that I do not want to be changed (ever).
Is it possible to set a MySQL row to READ-ONLY so that it cannot be updated in any way? If so, how?
If not, is it possible to set a permanent value in one of the columns of that row so that it cannot be changed? If so, how?
Thanks.
This is likely to be business logic, which probably doesn't belong in your data storage layer. However, it can nonetheless be accomplished using triggers.
You can create a BEFORE UPDATE trigger that raises an error if a "locked" record is about to be updated; since an error occurs before the operation is undertaken, MySQL ceases to proceed with it. If you also want to prevent the record from being deleted, you'd need to create a similar trigger BEFORE DELETE.
To determine whether a record is "locked", you could create a boolean locked column:
ALTER TABLE my_table ADD COLUMN locked BOOLEAN NOT NULL DEFAULT FALSE;
DELIMITER ;;
CREATE TRIGGER foo_upd BEFORE UPDATE ON my_table FOR EACH ROW
IF OLD.locked THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Cannot update locked record';
END IF;;
CREATE TRIGGER foo_del BEFORE DELETE ON my_table FOR EACH ROW
IF OLD.locked THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Cannot delete locked record';
END IF;;
DELIMITER ;
UPDATE my_table SET locked = TRUE WHERE ...;
Note that SIGNAL was introduced in MySQL 5.5. In earlier versions, you must perform some erroneous action that causes MySQL to raise an error: I often call an non-existent procedure, e.g. with CALL raise_error;
I cannot create an additional column on this table, but the row has a unique id in one of the columns, so how would I do this for that scenario?
Again, if you absolutely must place this logic in the storage layer—and cannot identify the locked records through any means other than the PK—you could hard-code the test into your trigger; for example, to "lock" the record with id_column = 1234:
DELIMITER ;;
CREATE TRIGGER foo_upd BEFORE UPDATE ON my_table FOR EACH ROW
IF OLD.id_column <=> 1234 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Cannot update locked record';
END IF;;
CREATE TRIGGER foo_del BEFORE DELETE ON my_table FOR EACH ROW
IF OLD.id_column <=> 1234 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Cannot delete locked record';
END IF;;
DELIMITER ;
But this is absolutely horrible and I would do almost anything to avoid it whenever possible.