Using triggers referring to different tables - mysql

I am trying to use a trigger so that it denies user entry if Boolean value from another table is unticked. How can I do this
TABLE A
IF
TABLE B attribute1 = 0 then, don't allow insert
TABLE B attribute1 = 1 then, allow insert
Sorry for the vauge description or zero code but I have no idea how to go about doing this

This should give you an starting point. Adjust table names and conditions according to your schema.
delimiter //
CREATE TRIGGER DENY_IF_TRUE
BEFORE INSERT ON [your table] FOR EACH ROW
BEGIN
DECLARE attr BOOLEAN;
-- 'set variable to attribute value
set #attr := (SELECT attribute FROM [your other table] WHERE [some condition] LIMIT 1);
IF #attr = TRUE THEN
-- 'this will make the trigger fail and therefore avoid the insert operation succeed'
CALL non_existent_function();
END IF;
END;
delimiter ;

Related

MySQL PROCEDURE using IF Statement with #Parameter Not Working

Why is the data not being inserted on the table when I execute the procedure, what seems to be lacking with the code?
I'm testing the procedure on phpMyAdmin > myDatabase > Procedures "Routines Tab" and clicking "Execute", prompts with a modal and ask for the values of "#idproc and #nameproc.
I tried with just the INSERT code it works, but when I add the IF condition it doesn't work.
Using XAMPP 8.0.3,
10.4.18-MariaDB
DELIMITER $$
CREATE DEFINER=`root`#`localhost:3307` PROCEDURE `testproc`(IN `idproc` INT, IN `nameproc` VARCHAR(100))
BEGIN
IF #idproc = 0 THEN
INSERT INTO testproc(
id,
name)
VALUES(
#idproc,
#nameproc
);
ELSE
UPDATE testproc
SET
id = #idproc,
name = #nameproc
WHERE id = #idproc;
END IF;
SELECT * FROM testproc;
END$$
DELIMITER ;
You mix local variables (their names have not leading #) and user-defined variables (with single leading #). This is two different variable types, with different scopes and datatype rules. Procedure parameters are local variables too.
So when you use UDV which was not used previously you receive NULL as its value - and your code works incorrectly. Use LV everywhere:
CREATE DEFINER=`root`#`localhost:3307`
PROCEDURE `testproc` (IN `idproc` INT, IN `nameproc` VARCHAR(100))
BEGIN
IF idproc = 0 THEN
INSERT INTO testproc (name) VALUES (nameproc);
ELSE
UPDATE testproc SET name = nameproc WHERE id = idproc;
END IF;
SELECT * FROM testproc;
END
You do not check does specified idproc value exists in the table. If it is specified (not zero) but not exists then your UPDATE won't update anything. Assuming that id is autoincremented primary key of the table I recommend to use
CREATE DEFINER=`root`#`localhost:3307`
PROCEDURE `testproc` (IN `idproc` INT, IN `nameproc` VARCHAR(100))
BEGIN
INSERT INTO testproc (id, name)
VALUES (idproc, nameproc)
ON DUPLICATE KEY
UPDATE name = VALUES(name);
SELECT * FROM testproc;
END
If specified idproc value exists in id column the row will be updated, if not then the new row will be inserted.
Additionally - I recommend you to provide NULL value instead of zero when you want to insert new row with specified nameproc value. NULL always cause autoincremented primary key generation whereas zero needs in specific server option setting.

Create trigger in mysql on insert where it compares fields of added row

I am new with mysql triggers, I have 2 tables in a database, one is called tasks and the other is task_rules.
Once a new task_rule is inserted, I want to compare the field time (which is a time object) to the current time.
if it is greater than the current time, I want to add a new row in tasks and set rid (in tasks) to id of the newly added rule, and the time field in tasks to the time field of the newly added row.
I am getting many syntax errors and i didnt know how to create this trigger.
BEGIN
DECLARE #time TIME
DECLARE #freq VARCHAR(400)
#time = NEW.time
#freq = NEW.frequency
IF (#time > NOW()) AND (#freq == 'daily') THEN
INSERT INTO task_rules ('rid', 'time') VALUES (NEW.id, #time)
END IF
END
Im doing it using phpmyadmin
1) user defined variable (those preceded with #) should not be declared see How to declare a variable in MySQL? 2) to assign a value to a variable you have to use the SET statement 3) every statement must be terminated - if you are using phpmyadmin and the default terminator is set to ; change it and terminate your statements in the trigger with ; see - https://dev.mysql.com/doc/refman/8.0/en/stored-programs-defining.html 4) null safe equals in mysql is not == from memory this should be <=> see https://dev.mysql.com/doc/refman/8.0/en/comparison-operators.html 5) you should probably set delimiters before and after the trigger 6) column names should be escaped with back ticks not single quotes. 7) for each row clause missing before begin statement.
try this
drop trigger if exists t;
delimiter $$
create trigger t after insert on task
for each row
BEGIN
DECLARE vtime TIME;
DECLARE vfreq VARCHAR(400);
set time = NEW.time;
set freq = NEW.frequency;
IF (vtime > NOW()) AND (vfreq <=> 'daily') THEN
INSERT INTO task_rules (`rid`, `time`) VALUES (NEW.id, vtime);
END IF;
END $$
delimiter ;
And do review https://dev.mysql.com/doc/refman/8.0/en/trigger-syntax.html

Update Trigger : Supertype/subtypes tables

I need some help about triggers. I’m currently developing a platform and a database in order to manage exams at my university. Here is my problem:
I have 1 supertype table, which contains all the persons registered on the platform. I have to be able to make the distinction for each person between the functions “Candidate” and “Examiner”. So I have my 2 subtype tables, one for all the candidates and one for all the examiners. To achieve that, I’m using insert triggers.
In addition, a person can be both Candidate and Examiner, but not at the same time. So after updating the supertype table, I also need a trigger to be able to delete a specific row from one of the two-subtype table and insert the user information on the other.
Here is a simplified design of these 3 tables:
My INSERT trigger :
ALTER TRIGGER [dbo].[role_insert]
ON [dbo].[alemp_persons]
FOR INSERT
AS
DECLARE #random_number int
SELECT #random_number = CAST(CAST(rand() as binary(2)) as int)
BEGIN
INSERT INTO dbo.alemp_candidates
(
id_person, random_number
)
SELECT id_person, # random_number
FROM INSERTED
WHERE function='Candidate'
INSERT INTO dbo.alemp_examiners
(
id_person
)
SELECT id_person
FROM INSERTED
Where function='Examiner'
END
GO
My UPDATE trigger :
ALTER TRIGGER [dbo].[role_update] ON [dbo].[alemp_persons]
AFTER UPDATE
AS
DECLARE #id_person int
DECLARE #newFunction int SELECT #newFunction=function FROM inserted
DECLARE #random_number int SELECT # random_number = CAST(CAST(rand() as binary(2)) as int)
IF #newFunction = 'Candidate'
BEGIN
DELETE
FROM dbo.alemp_examiners
WHERE id_person=#id_person
END
BEGIN
SET IDENTITY_INSERT dbo.alemp_candidates ON;
INSERT INTO dbo.alemp_candidates
(
id_person, random_number
)
SELECT #id_person, random_number
SET IDENTITY_INSERT dbo.alemp_candidates OFF;
END
IF #newFunction = 'Examiner'
BEGIN
DELETE
FROM dbo.alemp_candidates
WHERE id_person=#id_person
END
BEGIN
SET IDENTITY_INSERT dbo.alemp_examiners ON;
INSERT INTO dbo.alemp_examiners
(
id_person
)
SELECT #id_person
SET IDENTITY_INSERT dbo.alemp_examiners Off;
END
GO
As I said above, my INSERT trigger works as I want. However when I want to update the function of one person, I got an error :
Explicit value must be specified for identity column either when IDENTITY_INSERT is set
to ON or when a replication user is inserting into a NOT FOR REPLICATION identity column.
Some simple notes:
1) You should follow Mitch Wheat's advice and rewrite these triggers because inserted and deleted tables could have more than one row. For example, your trigger will have a bad behavior when is executed the next statement UPDATE [dbo].[alemp_persons] SET function = CASE WHEN id_person = 1 THEN 'Candidate' ELSE 'Examiner' END WHERE id_person IN (1,2) if the first person's function is 'Examiner' and the second person's function is 'Candidate'.
2) [dbo].[alemp_persons].function's data type should be [tiny]int or char(1) and not varchar(something greater than 1) (Where function='Examiner').
3) [dbo].[alemp_persons].function column should disallow Nulls.
4) [dbo].[alemp_persons].function column should has a CHECK constraint:
ALTER TABLE [dbo].[alemp_persons]
ADD CONSTRAINT CK_alemp_persons_function_Verify CHECK ( function IN ('Candidate', 'Examiner') );
5) It would be nice to add
a function column to [dbo].[alemp_candidates] and [dbo].[alemp_examiners] tables,
two check constraints on [dbo].[alemp_candidates] (function = 'Candidate') and [dbo].[alemp_examiners] (function = 'Examiner'),
an UNIQUE index on [dbo].[alemp_persons](id_person, function),
two FKs between [dbo].[alemp_candidates/examiners](id_person, function) and [dbo].[alemp_persons](id_person, function).
This way, you can be sure that [dbo].[alemp_candidates] table has only candidates and [dbo].[alemp_examiners] has only examiners and every person can be only candidate or examiner at one time.
6) You should disallow IDENTITY property for id_person columns in [dbo].[alemp_candidates] and [dbo].[alemp_examiners] table (SET IDENTITY_INSERT dbo.alemp_candidates ...).
8) And this statement IF #newFunction = 'Candidate' should raise an error because #newFunction data type is 'INT'.
9) And the AFTER UPDATE trigger on [dbo].[alemp_persons] table will move data between candidates and examiners tables (not tested):
ALTER TRIGGER [dbo].[role_update]
ON [dbo].[alemp_persons]
FOR UPDATE
AS
BEGIN
DECLARE #selected_rows TABLE (
id_person INT PRIMARY KEY, -- or BIGINT, look at alemp_person.id_person data type
new_function VARCHAR(50) NOT NULL -- look at alemp_person.function column data type
);
INSERT #selected_rows (id_person, new_function)
SELECT new.id_person, new.function
FROM inserted as new INNER JOIN deleted as old ON new.id_person = old.id_person
WHERE new.function <> old.function;
MERGER dbo.alemp_candidates AS dest
USING #selected_rows AS src ON dest.id_person = src.id_person
WHEN MATCHED THEN
DELETE
WHEN NOT MATCHED BY TARGET AND src.new_function = 'Candidate' THEN
INSERT (id_person, random_number)
VALUES (src.id_person, CONVERT(BINARY(2), CHECKSUM(NEWID()));
MERGER dbo.alemp_examiners AS dest
USING #selected_rows AS src ON dest.id_person = src.id_person
WHEN MATCHED THEN
DELETE
WHEN NOT MATCHED BY TARGET AND src.new_function = 'Examiner' THEN
INSERT (id_person)
VALUES (src.id_person);
END

sql server trigger for custom PK

I want to generate PK through trigger as it is custom PK.
It is like depending on the member type field, I want to generate member id which is PK.
e.g. if new record's member type is DGIA, then member id will be DGIA1, DGIA2, DGIA3 ...and so on... if member type is DGIL, then member id will be DGIL1, DGIL2, DGIL3 ...and so on...
So, how to write trigger for the same... I have tried as following but it is working for 1st record only.
ALTER TRIGGER [dbo].[next_member_id] ON [dbo].[DAD_MEMBERSHIP] AFTER INSERT
AS
BEGIN
DECLARE #COUNT INT
SET #COUNT=0;
SELECT #COUNT=ISNULL(MAX(CAST(SUBSTRING(DAD_MEMBERSHIP.MEMBER_ID,5,15) AS INT)),0)+1 FROM DAD_MEMBERSHIP where DAD_MEMBERSHIP.MEMBER_TYPE = DAD_MEMBERSHIP.MEMBER_TYPE
update DAD_MEMBERSHIP set DAD_MEMBERSHIP.MEMBER_ID = DAD_MEMBERSHIP.MEMBER_TYPE + CONVERT(varchar,#COUNT)
from DAD_MEMBERSHIP inner join inserted on DAD_MEMBERSHIP.MEMBER_TYPE = inserted.MEMBER_TYPE
END
Triggers operate by batch of records, you cannot assign to a scalar variable and expect it to work for more than one record. You need to rethink your whole process into a set-based process.
I solved the problem using following trigger
ALTER TRIGGER [dbo].[next_member_id]
ON [dbo].[DAD_MEMBERSHIP]
AFTER INSERT
AS
BEGIN
DECLARE #COUNT INT
SET #COUNT=0;
DECLARE #STR VARCHAR(5)
SET #STR=''
select #STR=i.MEMBER_TYPE from inserted i;
SELECT #COUNT=ISNULL(MAX(CAST(SUBSTRING(DAD_MEMBERSHIP.MEMBER_ID,5,15) AS INT)),0)+1
from DAD_MEMBERSHIP where MEMBER_TYPE=#STR
update DAD_MEMBERSHIP set DAD_MEMBERSHIP.MEMBER_ID = #STR + CONVERT(varchar,#COUNT)
from DAD_MEMBERSHIP inner join inserted i on i.MEMBER_TYPE=DAD_MEMBERSHIP.MEMBER_TYPE where DAD_MEMBERSHIP.MEMBER_ID is null
END

Mysql auto fill field based on value of two other fields?

I know its possible to autoincrement values, but i was wondering if its possible to fill a field based on the value of two other fields. I have a table with the fields:
CREATE TABLE pligg_links (
...
link_votes INT,
link_reports INT,
link_votes_total INT,
...
);
Field link_votes_total should hold the value of field link_votes subtracted from link_reports. So basically, this is the math equation: link_votes_total = link_votes - link_reports. Is this possible without having to use php to do it before data is stored?
Yes, this can be done by creating a trigger for BEFORE INSERT and another one for BEFORE UPDATE:
DELIMITER //
CREATE TRIGGER trig_mytable BEFORE INSERT ON my_table
FOR EACH ROW
BEGIN
SET NEW.link_votes_total = NEW.link_votes - NEW.link_reports;
END
//
CREATE TRIGGER trig_mytable BEFORE UPDATE ON my_table
FOR EACH ROW
BEGIN
SET NEW.link_votes_total = NEW.link_votes - NEW.link_reports;
END
//
DELIMITER ;
Further Reading:
MySQL 5.1 Reference Manual: CREATE TRIGGER Syntax
See:http://dev.mysql.com/doc/refman/5.1/en/triggers.html
DELIMITER //
CREATE TRIGGER bir_links
BEFORE INSERT ON links
FOR EACH ROW
BEGIN
SET link_votes_total = NEW.link_votes - NEW.link_reports;
END;
//
CREATE TRIGGER bur_links
BEFORE UPDATE ON links
FOR EACH ROW
BEGIN
SET link_votes_total = NEW.link_votes - NEW.link_reports;
END;
//
DELIMITER ;